函数和函数式编程

函数和函数式编程

函数

参数传递

不可变对象和可变对象
  • 对于不可变对象来说,实参和被修改后的形参是两个对象。**对于变量的重新的赋值操作,相当于重新生成了一个新的对象,**类似于值传递。

    • 数值类型,tuple类型,string类型**等都是不可变对象
    def fun(arg):
        print('传入参数的id:{}'.format(id(arg)))
        arg = 1 # 修改参数的值
        print('修改后参数的id:{}'.format(id(arg)))
        return arg
    
    a = 2
    print('函数被调用之前的实参的id:{}'.format(id(a)))
    res = fun(a)
    print('函数被调用之后的实参的id:{}'.format(id(a)))
    print('函数返回的形参的id:{}'.format(id(res)))
    
    # 结果
    函数被调用之前的实参的id1901637888
    传入参数的id1901637888
    修改后参数的id1901637856
    函数被调用之后的实参的id1901637888
    函数返回的形参的id1901637856
    
    
  • 对于可变对象来说,实参和被修改后的形参是一个对象,类似于引用传递

    • 列表list,字典dict等都是可变对象。
    def fun(arg):
        print('传入参数的id:{}'.format(id(arg)))
        arg.append(1) # 修改参数的值
        print('修改后参数的id:{}'.format(id(arg)))
        return arg
    
    a = [2]
    print('函数被调用之前的实参的id:{}'.format(id(a)))
    res = fun(a)
    print('函数被调用之后的实参的id:{}'.format(id(a)))
    print('函数返回的形参的id:{}'.format(id(res)))
    
    # 结果
    函数被调用之前的实参的id2180016515784
    传入参数的id2180016515784
    修改后参数的id2180016515784
    函数被调用之后的实参的id2180016515784
    函数返回的形参的id2180016515784
    
  • 结论:对于不可变对象,如果进行重新指向,则是生成实参和形参是两个对象;对于可变对象,形参和实参是同一个对象。

参数

必须参数
关键字参数
默认参数
不定长参数
封包与解包
封包
  • 将多个值付给一个变量时,python会自动将这些值封装成元组,这个特性称为封包

    a = 1, 2, 'a'
    print(a, type(a)) 
    
    # 结果
    (1, 2, 'a') <class 'tuple'>
    
  • 当函数有多个返回值是,封包

    def fun():
        return 1, 2, 'a'
    res = fun()
    print(res, type(res))
    
    # 结果
    (1, 2, 'a') <class 'tuple'>
    
解包
  • 接受函数返回值

    def test():
        return 1, 2, 3
    
    a, b, c = test()
    print(a, b, c) 
    
    # 结果
    1 2 3
    
    def test():
        return 1, 2, 3
    
    a, *b = test()
    print(a, b)
    
    # 结果
    1 [2, 3]
    
  • 传递参数

    def fun1(*args):
        print(type(args), args)
        
    a = (2, 4, 6)
    func1(*a)    # 将元组解包成可变参数
    
    # 结果
    <class 'tuple'> (2, 4, 6)
    
    def fun2(**kwargs):
        print(type(kwargs), kwargs)
    
    b = {'一': 1, '二': 2}
    fun2(**b)  # 将字典解包成关键字参数
    
    # 结果
    <class 'dict'> {'一': 1, '二': 2}
    
带一个星号*
  • 一般在函数定义时,用 *args 表示。

    • 此时args定义在函数的中的数据类型是元组tuple类型。
    def fun(*args):
        print(type(args), args)
    
    # 方式一
    fun(2, 4, 6)
    # 方式二
    a = (2, 4, 6)
    fun(*a) # 将元组解包成可变参数
    
    # 结果
    <class 'tuple'> (2, 4, 6)
    <class 'tuple'> (2, 4, 6)
    
带两个星号**
  • 一般在函数定义时,用 **kwargs表示

    • 此时kwargs定义咋函数中的数据类型是字典类型
    def fun(**kwargs):
        print(type(kwargs), kwargs)
    
    # 方式一
    fun(a=1, b=2)
    # 方式二
    b = {'a': 1, 'b': 2}
    fun(**b)  # 将字典解包成关键字参数
    
    # 结果
    <class 'dict'> {'a': 1, 'b': 2}
    <class 'dict'> {'a': 1, 'b': 2}
    

函数式编程

  • 重要特征:(1)将函数作为形参传入,(2)将函数作为返回值返回

返回函数

闭包
def outerFun(*args): # 定义外部函数,并接受不定长参数
    def innerFun(): # 定义内部函数
        ax = 0 # 定义局部变量
        for n in args: # 使用enclosing
            ax = ax + n
        return ax
    return innerFun # 返回内部函数

fun = outerFun(1, 2) # 使用返回的函数
res = fun() # 将返回的函数调用,并将函数的运行结果赋值给res
print(res)

# 结果
3

在函数outerFun中又定义了函数innerFun,并且,内部函数innerFun可以引用外部函数outerFun的参数和局部变量args,当outerFun返回函数innerFun时,相关参数和变量都保存在返回的函数中,这种称为“闭包(Closure)”

  • 注意点:返回时,写的是内部函数的函数名,而不是返回内部函数的调用。

装饰器 decorator

  • 由于函数也是一个对象,而且函数对象可以被赋值给变量,所以,通过变量也能调用该函数。

  • 基本形式,无参

    # 定义一个需要被包装的函数
    def need_decorated_fun(num):
        print(num)
    
    # 定义一个装饰器
    def decorated(fun): # 装饰器的参数是一个被装饰函数的名称
        def wrapper(*args, **kwargs): # 具体的装饰实现,使用wrapper,接受不定长参数,封包
            print('function name:%s' % fun.__name__) # 打印被装饰函数的__name__属性,
                                                      # 函数增强
            return fun(*args, **kwargs) # 返回被装饰函数的运行结果,解包
        return wrapper
    
    need_decorated_fun = decorated(need_decorated_fun)
    need_decorated_fun(2)
    print('修改后原需要被装饰函数的函数名为:%s' % need_decorated_fun.__name__)
    
    # 结果
    function name:need_decorated_fun
    2
    修改后原需要被装饰函数的函数名为:wrapper
    
    • 借助@符号,无参
    # 定义一个装饰器
    def decorated(fun): # 装饰器的参数是一个被装饰函数的名称
        def wrapper(*args, **kwargs): # 具体的装饰实现,使用wrapper,接受不定长参数,封包
            print('function name:%s' % fun.__name__) # 打印被装饰函数的__name__属性,函数增强
            return fun(*args, **kwargs) # 返回被装饰函数的运行结果,解包
        return wrapper
    
    # 使用@符号进行装饰需要被装饰的函数
    @decorated
    def need_decorated_fun(num):
        print(num)
    
    need_decorated_fun(112)
    print('修改后原需要被装饰函数的函数名为:%s' % need_decorated_fun.__name__)
    
    # 结果
    function name:need_decorated_fun
    112
    修改后原需要被装饰函数的函数名为:wrapper
    

    @decorated放在need_decorated_fun之上,相当于need_decorated_fun = decorated(need_decorated_fun)

  • 基本形式,有参

    # 定义一个需要被包装的函数
    def need_decorated_fun(num):
        print(num)
    
    # 定义一个装饰器,含参数
    def para_decorated(para): # para表示需要额外传入的参数
        def decorated(fun): #装饰器的参数是一个被装饰函数的名称
            def wrapper(*args, **kwargs): # 具体的装饰实现,使用wrapper,接受不定长参数,封包
                print('additional parameter:%s' % para)
                print('function name:%s' % fun.__name__) # 打印被装饰函数的__name__属性,                                                       # 函数增强
                return fun(*args, **kwargs) # 返回被装饰函数的运行结果,解包
            return wrapper
        return decorated
    
    need_decorated_fun = para_decorated('Para Decorated')(need_decorated_fun)
    
    need_decorated_fun(112)
    print('修改后原需要被装饰函数的函数名为:%s' % need_decorated_fun.__name__)
    
    # 结果
    additional parameter:Para Decorated
    function name:need_decorated_fun
    112
    修改后原需要被装饰函数的函数名为:wrapper
    
    
    • 借助@符号,有参

      # 定义一个装饰器,含参数
      def para_decorated(para): # para表示需要额外传入的参数
          def decorated(fun): #装饰器的参数是一个被装饰函数的名称
              def wrapper(*args, **kwargs): #具体装饰实现,用wrapper,接受不定长参数,封包
                  print('additional parameter:%s' % para)
                  print('function name:%s' % fun.__name__) # 打印被装饰函数__name__属性                                                       # 函数增强
                  return fun(*args, **kwargs) # 返回被装饰函数的运行结果,解包
              return wrapper
          return decorated
      
      @para_decorated('Para Decorated')
      def need_decorated_fun(num):
          print(num)
          
      need_decorated_fun(112)
      print('修改后原需要被装饰函数的函数名为:%s' % need_decorated_fun.__name__)
      
      # 结果
      additional parameter:Para Decorated
      function name:need_decorated_fun
      112
      修改后原需要被装饰函数的函数名为:wrapper
      

      @para_decorated('Para Decorated')放在need_decorated_fun之前,相当于need_decorated_fun = para_decorated('Para Decorated')(need_decorated_fun)

  • 存在问题:修改后原需要被装饰函数的函数名为:wrapper,我们希望即使需要被装饰的函数在装饰完毕后仍具有原来的函数名

    • 方式一:把原始函数的__name__等属性复制到wrapper()函数中,编写wrapper.__name__ = func.__name__这样的代码
    • 方式二:使用@functools.wraps(被装饰函数名),在functools模块中

    在不需要现实定义函数的时候,传入匿名函数更方便

装饰器完整形式,无参
import functools
# 定义一个装饰器
def decorated(fun): # 装饰器的参数是一个被装饰函数的名称
    @functools.warps(fun) # 保证被装饰的函数的函数名不变  
    def wrapper(*args, **kwargs): # 具体的装饰实现,使用wrapper,接受不定长参数,封包
        print('function name:%s' % fun.__name__) # 打印被装饰函数的__name__属性,函数增强
        return fun(*args, **kwargs) # 返回被装饰函数的运行结果,解包
    return wrapper

# 使用@符号进行装饰需要被装饰的函数
@decorated
def need_decorated_fun(num):
    print(num)

need_decorated_fun(112)
print('修改后原需要被装饰函数的函数名为:%s' % need_decorated_fun.__name__)

# 结果
function name:need_decorated_fun
112
修改后原需要被装饰函数的函数名为:need_decorated_fun
装饰器完整形式,有参
import functools
# 定义一个装饰器,含参数
def para_decorated(para): # para表示需要额外传入的参数
    def decorated(fun): #装饰器的参数是一个被装饰函数的名称
        @functools.warps(fun) # 保证被装饰的函数的函数名不变
        def wrapper(*args, **kwargs): #具体装饰实现,用wrapper,接受不定长参数,封包
            print('additional parameter:%s' % para)
            print('function name:%s' % fun.__name__) # 打印被装饰函数__name__属性                                                       # 函数增强
            return fun(*args, **kwargs) # 返回被装饰函数的运行结果,解包
        return wrapper
    return decorated

@para_decorated('Para Decorated')
def need_decorated_fun(num):
    print(num)
    
need_decorated_fun(112)
print('修改后原需要被装饰函数的函数名为:%s' % need_decorated_fun.__name__)

# 结果
additional parameter:Para Decorated
function name:need_decorated_fun
112
修改后原需要被装饰函数的函数名为:need_decorated_fun

匿名函数

  • 格式:lambda 参数列表:表达式,函数的返回值是表达式的值
  • 本质:匿名函数本质上就是个函数,完全可以用显示定义的方式给出
f = lambda x:x*x # 将一个匿名函数赋值给一个对象
res = f(3) # 调用函数
print(res)

# 结果
9
  • 匿名函数,在一些高阶函数,或其他框架的函数会被经常性的使用

高阶函数

  • 变量可以指向函数;函数名也是变量;所以变量可以指向函数名
map
  • map(函数(仅含有一个参数),Iterable对象),返回值为Iterator

  • 其中传入的函数,含有一个参数,对应于Iterator中的每一个元素

  • 因为Iterator是惰性序列,可以通过list()函数将整个序列都计算出来,并返回一个list

    def fun(x):
        return  2 * x
    
    res1 = map(fun, [1, 2])
    print(type(res1))
    print(res1)
    res2 = list(res1) #通过list()函数将整个序列都计算出来,并返回一个list
    print(res2)
    
    # 结果
    <class 'map'>
    <map object at 0x000002719C6DD278>
    [2, 4]
    
    • 使用匿名函数

      res1 = map(lambda x: 2 * x, [1, 2])
      print(type(res1))
      print(res1)
      res2 = list(res1) #通过list()函数将整个序列都计算出来,并返回一个list
      print(res2)
      
      # 结果
      <class 'map'>
      <map object at 0x0000018901D5C7B8>
      [2, 4]
      
reduce
  • reduce(函数(仅含有两个参数),Iterable)

  • reduce把一个函数作用在一个序列[x1, x2, x3, ...]上,这个函数必须接收两个参数,reduce把结果继续和序列的下一个元素做累积计算

  • 返回值是函数的返回值,并不是一个Iterator

    reduce(f, [x1, x2, x3, x4]) = f(f(f(x1, x2), x3), x4)
    
  • reducefuntools模块中

    from functools import reduce
    
    def fun(x, y):
        return x + y
    
    res1 = reduce(fun, [1, 2, 3, 4])
    print(type(res1))
    print(res1)
    
    # 结果
    <class 'int'>
    10
    
    • 使用匿名函数

      from functools import reduce
      
      res1 = reduce(lambda x,y: x - y, [1, 2, 3, 4])
      print(type(res1))
      print(res1)
      
      # 结果
      <class 'int'>
      -8
      
filter
  • filter用于过滤序列,其中满足条件返回True的元素被保留下来,不满足条件返回False的元素不被保留

  • filter(函数(仅含一个参数), Iterable)

  • 其中函数仅含一个参数,对应Iterable中的每一个参数

  • 最终的返回值是一个Iterator,可以通过list()函数将整个序列都计算出来,并返回一个list

    def isOdd(x):
        return x % 2 == 1
    res1 = filter(isOdd, [1, 2, 3, 4])#[1, 2, 3, 4] →isOdd→ [True, False, True, Flase]
    								  #满足条件对应True位置上的元素被保留下来
    print(type(res1))
    print(res1)
    res2 = list(res1) # #通过list()函数将整个惰性序列都计算出来,并返回一个list
    print(res2)
    
    # 结果
    <class 'filter'>
    <filter object at 0x000001A98A27D278>
    [1, 3]
    
sorted
  • 排序的核心是比较两个元素的大小。如果是数字,我们可以直接比较;

  • 如果是字符串或者两个dict呢?直接比较数学上的大小是没有意义的,因此,比较的过程必须通过函数抽象出来

  • sorted(Iterable, key, reverse=False),关键就是如何写这个key;其中Iterable中的每个元素之间都是应该可比较的。

    • 如数值1和字符串'1'之间不可比较,此时运用在key中的比较,会产生TypeError异常
  • 最终的返回结果是列表list类型,而不是Iterator

    • 默认情况

      res = sorted([1, 3, 2]) # 数值默认按数值由小到大排序
      print(res)
      
      # 结果
      [1, 2, 3]
      
      res = sorted(['1', 'a', 'A']) # 字符串默认按ASCII码由小到大排序
      print(res)
      
      # 结果
      ['1', 'A', 'a']
      
    • 按照key对应的函数规则进行排序

      • key指向的函数中仅包含一个参数时,该参数对应Iterable中的每个第一级元素
      res =  sorted([6, 5, -1, 9, -11], key=abs) #在函数abs的作用下,各元素的权重为
                                              #[6, 5, 1, 9, 11],对应权重的权重越大,越靠后
      print(res)   
      
      # 结果
      [-1, 5, 6, 9, -11]
      
      res = sorted([6, 5, -1, 9, -11], key=lambda x: -x)
                                      #对应的权重变为[-6, -5, 1, -9, 11],
       								#然后按照权重排序,权重越大,越靠后
      print(res)
      
      # 结果
      [9, 6, 5, -1, -11]
      
    • 字典按照keyvalue排序

      • 字典按照key排序

        my_dict = {"k2": 5, "k3": 6, "k1": 4, "k0": 3}
        new_list = sorted(my_dict.items(), key=lambda x:x[1])
        		        # my_dict.items()将键值对转为可迭代的对象,[(键,值),...]
        				# x表示items中的一个元素, x[0]表示按第一个位置key排序
        print(my_dict)
        print(my_dict.items())
        print(new_list)
        
        # 结果
        {'k2': 5, 'k3': 6, 'k1': 4, 'k0': 3}
        dict_items([('k2', 5), ('k3', 6), ('k1', 4), ('k0', 3)])
        [('k0', 3), ('k1', 4), ('k2', 5), ('k3', 6)]
        
      • 字典按照value排序

        my_dict = {"k2": 5, "k3": 6, "k1": 4, "k0": 3}
        new_list = sorted(my_dict.items(), key=lambda x:x[1])
        		        # my_dict.items()将键值对转为可迭代的对象,[(键,值),...]
        				# x表示items中的一个元素, x[1]表示按第二个位置value排序
        print(my_dict)
        print(my_dict.items())
        print(new_list)
        
        # 结果
        {'k2': 5, 'k3': 6, 'k1': 4, 'k0': 3}
        dict_items([('k2', 5), ('k3', 6), ('k1', 4), ('k0', 3)])
        [('k0', 3), ('k1', 4), ('k2', 5), ('k3', 6)]
        

偏函数

  • 偏函数所在模块functools

    a1 = int('123') #示123在十2进制下对应的十进制的数,1*10^2+2*10^1+3*10^0=123
    a2 = int('123', base=8) #表示123在8进制下对应的十进制的数,1*8^2+2*8^1+3*8^0=83
    print(a1)
    print(a2)
    
    # 结果
    123
    83
    
  • 自定义个函数

    def int2(x, base=2):
        return int(x, base) #返回一个x在2进制下表示的十进制的数
    
    res = int2('1001')
    print(res)
    
    # 结果
    9
    
  • 使用偏函数

    import functools
    int2 = functools.partial(int, base=2)
    res = int2('1001')
    print(res)
    
    # 结果
    9
    

变量访问顺序:LEGB

  • LEGB其他python解释器查找变量的顺序:LEGB

    • Local:函数内部作用域

    • Enclosing function:内嵌函数对函数内部变量的应用,即闭包(闭合函数)

    • Global:全局作用域

    • Build-in:python解释器默认导入的一些list/tuple等

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值