Python学习笔记之函数式编程

一、高阶函数

    把函数作为参数传入,这样的函数成为高阶函数,函数式编程就是指这样高度抽象的编程范式 。

    高阶函数:

                   实参:可以是一个函数的函数名;

                   返回值:可以是一个函数的函数名。

    (一)内置高阶函数map()

             map()函数会根据提供的函数对指定的序列做映射。其语法如下:

       map(function, iterable, ...)  #function --函数  iterable --一个或多个序列

             第一个参数function以参数序列中的每一个元素调用function函数,返回包含每次function函数的返回

      值的新列表(python2)或迭代器(python3)                 

    def fun(x):
        return x**2
    print(list(map(fun,range(1,10))))    #将一个存储0~9的列表的每一个元素平方

    输出:

    [1, 4, 9, 16, 25, 36, 49, 64, 81]
        当function函数拥有两个参数时,map()函数的可迭代的对象也就需要两个参数
    def fun_1(x,y):
        return x+y
    print(list(map(fun_1,range(1,11),range(1,11))))   #计算两个列表每个元素相加的和

    输出:

    [2, 4, 6, 8, 10, 12, 14, 16, 18, 20]

    (二)内置高阶函数reduce()


             Python2中reduce()函数在全局名字空间中,可以直接使用,在Python3中已经从全局名字空间中移除

      了,被放置在function模块里面,如果想要使用它,则需要通过引入functools模块来调用reduce()函数。

  from functools import reduce

             reduce()函数的语法如下:

  reduce(function,iterable[,initializer])  function--函数, iterable --可迭代对象 , initializer --可选,初始参数.

            redeuce()函数会对参数序列中的元素进行累积,函数讲一个数据集合(链表,元组)中的所有有数据进行

     下列操作:用传给reduce中的函数function(必须有两个参数)先对集合中的1,2个元素进行操作,在将得到的

     结果与第三个数据用function函数运算,最后得到一个结果。

 from functools import reduce
 def add(x,y):
     return x+y

 print(reduce(add,range(1,11)))   #((((1+2)+3)+4)+5)+6)+...   计算1~10的元素之和. 
输出:
    55

    (三)内置高阶函数filter()

              filter()函数用于过滤序列,过滤掉不符合条件的元素,返回由符合条件元素组成的新列表,该函数接收

       两个参数,第一个为函数,第二个为序列序列的每个元素作为参数传递给函数进行判断,然后返回True或

       Fase,最后将返回True的元素放到新的列表中。

    filter(function,iterable)  #function --判断函数 , iterable -- 可迭代的对象
        例子:
        def chose_odd(num):
        return (num % 2 == 0)

        print(list(filter(chose_odd,range(1,11))))

        输出:

        [2, 4, 6, 8, 10]
        

        注意:

                关于filter()方法,在python2和python3中有所不同,python2中返回的是过滤后的列表, 而python3

        中返回到是一个filter类,filter类实现了__iter__和__next__方法, 可以看成是一个迭代器, 有惰性运算的特性,

        相对python2提升了性能, 可以节约内存.

    (四)内置高阶函数sorted()

                sorted()对所有的可迭代对象进行排序操作,
         sorted(iterable, [,cmp[,key[,reverse]]])        
    iterable --     可迭代对象。
    cmp -- 	    比较的函数,这个具有两个参数,参数的值都是从可迭代对象中取出,
	            此函数必须遵守的规则为,大于则返回1,小于则返回-1,等于则返回0。
    key -- 	    主要是用来进行比较的元素,只有一个参数,具体的函数的参数就是取自
	            于可迭代对象中,指定可迭代对象中的一个元素来进行排序。
    reverse --      排序规则,reverse = True 降序 , reverse = False 升序(默认)。

二、练习

    (一)第一题

            给定一个整形数组, 将数组中所有的0移动到末尾, 非0项保持不变;在原始数组上进行移动操作, 勿

     创建新的数组;

       # 输入:
        第一行是数组长度, 后续每一行是数组的一条记录;
        4
        0
        7
        0
        2
      # 输出:
        调整后数组的内容;
        7
        2
        0

        0

    def fun(num):
        if num == 0:
            return 1
        else:
            return 0
    len_num = int(input('数组长度'))
    list_num = [int(input()) for i in range(len_num)]
    print(sorted(list_num,key=fun)) #让sorted根据列表的每一个元素进行fun函数操作之后的返回值进行排序,将所有为返回值为1的元素,排在前面
    (二)第二题

            输入一个只有空格与数字的字符串,将其转化为整形存入列表中,

    str_num = input()
    print(list(map(int,str_num.split())))   #map 将序列的每一个元素一次带入到功能函数中去进行计算,并返回一个map对象  
    (三)第三题

          计算10的阶乘

    def jiecheng(x,y):
        return x*y
    print(reduce(jiecheng,range(1,11)))
    (四)第四题
            1. 给定一个列表,li = [[], "hello", " ", "westos", () ]
            2. 删除列表中的空元素, 包括空列表,空元组, 空集合, 空字典,空字符串;
    li = [[], "hello", " ", "westos", () ]
    def fun(element):
        if element:
            return True
        else:
            return False
    print(list(filter(fun,li)))
    (五)第五题

           1、有下面列表存放的是手机的序号,品牌,销量,价格

              info = [

                        ['001', 'apple', 1000, 2],
                        ['002', 'xiaomi', 10, 2000],
                        ['003', 'Oppo', 200, 1900],
                        ['004', 'computer', 900, 5000]
               ]

            现根据销量对他们进行升序排序。

    info = [
        ['001', 'apple', 1000, 2],
        ['002', 'xiaomi', 10, 2000],
        ['003', 'Oppo', 200, 1900],
        ['004', 'computer', 900, 5000]
    ]
    def sorted_sales(items):
        return items[2]
    print(sorted(info,key = sorted_sales))

          2、有下面字典根据每个元素的['count']进行排序

        info1 = {
                '001':{
                        'name':'apple',
                        'count':1000,
                        'price':2
                        },
                '002': {
                        'name': 'xiaomi',
                        'count': 10,
                        'price': 2000
                      },
                '003': {
                        'name': 'Oppo',
                        'count': 200,
                        'price': 1900
                      }
              }

        代码:

    def return_count(items):
        return items['count']

    print(sorted(info1.values(),key=return_count))

三、匿名函数

          1. 匿名函数的关键字为  lambda, 冒号前面是形式参数, 冒号后面是返回值;
          2. 匿名函数的形式参数可以是: 必选, 默认, 可变, 关键字参数

        练习:使用匿名函数实现第二节的1,3,5练习题。

          第一题:

    len_num = int(input('数组长度'))
    li=[int(input()) for i in range(len_num)]
    print(sorted(li,key=lambda x : 1 if x==0 else 0))  #匿名函数方式

        第三题:

     print(reduce(lambda x,y:x*y,range(1,11)))
        第五题:

            1、

        print(sorted(info, key= lambda items : items[2]))  #匿名函数方法

            2、

        print(sorted(info1.values(),key = lambda items : items['count']))

四、返回值是函数名的高阶函数

        高阶函数除了可以接受函数作为参数外,还可以把函数作为结果值返回。

        我们来实现一个可变数的求和。通常情况下,求和函数是这样定义的:

    def calc_sum(*args):
            ax = 0
            for n in args:
                ax = ax + n
            return ax

         但是,如果不需要立刻求和,而是在后面代码中,根据需要在计算怎办?我们可以不反回求和的结果,

  而是返回求和的函数!   

    def lazy_sum(*args):
        def sum():
            ax = 0
            for n in args:
                ax = ax + n
             return ax
        return sum

        当我们调用 lazy_sum()时,返回的并不是求和结果,而是求和函数 。在这个例子中,我们在函数 lazy_sum

 中又定义了函数 sum,并且,内部函数 sum 可以引用外部函数,外部函数 lazy_sum 的参数和局部变量,当

 lazy_sum 返回函数 sum 时,相关参数和变量都保存在返回的函数中,这种称为闭包(Closure的程序

 构拥有极大的威力。

        闭包: 函数里面嵌套函数;

五、装饰器

    (一)普通装饰器

       由于函数也是一个对象,而且函数对象可以被赋值给变量,所以,通过变量也能调用该函数。当我们需要增强某个函数的功能时,但又不想改变原函数,那么我们可以使用装饰器对函数的功能进行增加。

       例如:我们编写一个timeit的装饰器用来装饰函数的执行时间的装饰器。

    def timeit(fun):  
        def wrapper(x,y):
            start_time = time.time()
            fun(x,y)   
            end_time = time.time()
            print("%s函数运行时间为%s" % (fun.__name__, end_time - start_time))
        return wrapper

        装饰器的本质也是一个函数,不过装饰器函数的返回值和它的参数都是一个函数,而且装饰器函数的定义中

 还嵌套了一个函数的定义。

        如上:当我们的解释器在运行时,def 视为函数定义,则timeit中的内容不会被执行,只有当timeit()函数被调用时,才会执行timeit()的函数体,运行代码。def wrapper() 和return wrapper() 两个语句,并调用wrapper()函数,执行装饰器参数函数fun(),和装饰器的内容。

        装饰器在使用时,需要我们在函数定义的前面加上@timeit(@装饰器的名字),例如:

    @timeit 
    def hello():
        time.sleep(0.04)
        print("hello")
    
    hello()

       装饰器的使用语句@timeit 的实质是将 hello = timeit(hello)  这样一个过程,将被修饰的函数的函数名当做实参传入装饰器,并用被装饰的函数的函数名来接收装饰器的返回值。

 练习:

    1. 创建add_log装饰器, 被装饰的函数打印日志信息;

    2. 日志格式为: [字符串时间] 函数名: xxx, 运行时间:xxx, 运行返回值结果:xxx,并自己创建一个函数测结果

import time
import functools
def add_log(fun):
    @functools.wraps(fun)      # 不会改变原函数的属性;
    def wrapper(x, y):
        start_time = time.time()
        res = fun(x, y)
        end_time = time.time()          
        
        #time.ctime()  获取系统字符串时间
        print("[%s] 函数名: %s 运行时间:%s  运行返回值结果: %s" % (time.ctime(),fun.__name__, end_time - start_time,res))
    return wrapper

@add_log
def add(x,y):
    return x+y
add(7,5)

结果:   

[Mon May 14 15:10:59 2018] 函数名: add 运行时间:1.9073486328125e-06  运行返回值结果: 12 
    (二)带参数的装饰器
          带参数的装饰器:在原来的装饰器外面加上一个函数,用来接收装饰器的参数
    def addLog(type):    
        def add_log(f):
            @functools.wraps(f)
            def wrapper(*args, **kwargs):
                start_time = time.time()
                res = f(*args, **kwargs)
                end_time = time.time()
                print("日志信息:[%s] [%s] %s 调试方式 %s 运行时间为%.5f s 运行结果为%s" %(
                    type.title(),time.ctime(),type,f.__name__,end_time-start_time,res))
                return res
            return wrapper
        return add_log
        调用上面的装饰器
    @addLog(type="debug")    传入参数
    def add(x,y):
        time.sleep(random.random())
        return x+y

  练习:写一个装饰器用来判断函数所接收的参数是不是我们所指定的类型.       

    import functools
    import copy
    def juge_type(*type_tmp):
        def required_ints(fun):
            @functools.wraps(fun)
            def wrapper(*args,**kwargs):
                for num in args:
                    if not isinstance(num ,type_tmp):
                        type_tmp_list = str(copy.deepcopy(type_tmp))
                        raise TypeError('参数必须是%s'%type_tmp_list)
                else:
                    res = fun(*args,**kwargs)
                    return res
            return  wrapper
        return required_ints

  调用:

@juge_type(int,float)
def add(x,y):
    return x+y
print(add(4,'4'))     

输出:

Traceback (most recent call last):
  File "/root/PycharmProjects/kaoshi/add_log.py", line 52, in <module>
    print(add(4,'4'))
  File "/root/PycharmProjects/kaoshi/add_log.py", line 41, in wrapper
    raise TypeError('参数必须是%s'%type_tmp_list)
TypeError: 参数必须是(<class 'int'>, <class 'float'>)
    (三)多个装饰器的执行顺序。

        当我们一个函数有两个甚至多个装饰器去修饰的时候,那么我们装饰器的执行顺序是什么呢?

def decorator_a(func):
    print('Get in decorator_a')
    def inner_a(*args, **kwargs):
        print('Get in inner_a')
        return func(*args, **kwargs)
    return inner_a
def decorator_b(func):
    print('Get in decorator_b')
    def inner_b(*args, **kwargs):
        print('Get in inner_b')
        return func(*args, **kwargs)
    return inner_b

@decorator_b   #  f =  decorator_b(inner_a)   # f -> inner_b
@decorator_a   # f = decorator_a(f)    # f -> inner_a
def f(x):
    print('Get in f')
    return x * 2

f(1)        #f ->inner_b  

        当我们存在多个装饰器是,在函数定义部分,装饰器的调用顺序是从下至上执行的,如上代码,我们的装饰调用时,先执行的是decorator_a  此时函数名f指向inner_a函数,下来的本质就是讲inner_a函数放到装饰器decorator_b中,使得,函数名f重新指向inner_b ,因此装饰器执行时,又是从上线下的顺序,先执行inner_b ,在执行inner_a函数。

输出:

    Get in decorator_a
    Get in decorator_b
    Get in inner_b
    Get in inner_a
    Get in f

练习:写两个装饰器,一个用来判断用户是否登录,另一个判断当前的用户是不是管理员。

import functools
import inspect
login_seesion = ['root','student','redhat']    #存放已经登陆的用户的名称的列表

# 该装饰器用来判断当前用户是否为管理员,是才可以添加学生信息
def is_admin(fun):                          # fun=add_student
    @functools.wraps(fun)
    def wapper2(*args, **kwargs):           # kwargs = {'name':'root'}
        #inspect.getcallargs()返回一个字典,key值为:形参 , value值为实参
        inspect_name = inspect.getcallargs(fun,*args,**kwargs)
        print(inspect_name)
        if inspect_name.get('name') == 'root':
            temp = fun(*args, **kwargs)
            return temp
        else:
            print("not root/admin user, no permisson add student")
    return wapper2
#该装饰器用来判断该用户是否登陆成功
def is_login(fun):
    @functools.wraps(fun)
    def wapper1(*args,**kwargs):
        if args[0] in login_seesion:
            tmp = fun(*args,**kwargs)
            return tmp
        else:
            print('Error!!')
    return wapper1



@is_login         # add_studnet = is_login(wapper1)       add_student -> wapper1()
@is_admin         # add_student = is_admin(add_student)   add_student -> wapper2()
def add_student(name):   #形参:name
    print('添加学生信息')

add_student('root')

猜你喜欢

转载自blog.csdn.net/m0_37717595/article/details/80274099