《流畅的Python》第三部分 把函数视作对象 【一等函数】【使用一等函数实现设计模式】【函数装饰器和闭包】

第三部分

第5章 一等函数

一等对象
  • 在运行时创建
  • 能赋值给变量或数据结构中的元素
  • 能作为参数传递给函数
  • 能作为函数的返回结果

在Python中,所有函数都是一等对象

函数是对象

函数本身是 function 类的实例。

高阶函数
  • 接受函数为参数,或者把函数作为结果返回的函数
  • 内置高阶函数:map, filter, reduce
  • 列表推导式或生成器推导式同时具有 map 和 filter 两个函数的功能
类的调用
  • 调用类的过程:运行类的 __ new __ 方法创建一个实例,然后运行 __ init __ 方法,初始化实例,最后返回实例给调用方
  • 如果类定义了 __ call __ 方法,那么类的实例可以作为函数调用*
  • 使用内置函数 callable() 判断对象是否可调用

? yield 生成器函数

函数参数

可以通过 inspect.signature( func ) 查看函数的参数信息

def func(a, b, c, *var, d, **kw):
  '''
  a, b, c:  位置参数,关键字参数
  var:      可变参数
  d:        仅限关键字参数
  kw:       可变关键字参数
  '''
  print("a=",a)
  print("b=",b)
  print("c=",c)
  print("var=",var)
  print("d=",d)
  print("kw=",kw)

func('a','b','c','var1','var2',d='d',kw1='kw1',kw2='kw2')
# a= a
# b= b
# c= c
# var= ('var1', 'var2')
# d= d
# kw= {'kw1': 'kw1', 'kw2': 'kw2'}
  • 位置参数(POSITIONAL_OR_KEYWORD)
    • 参数定义时,前面的 a, b, c 参数可以直接传入值,并按位置接收
      • def fun(a,b,c)
      • fun(1,2,3)
      • a,b,c = 1,2,3
  • 关键字参数
    • 参数传入的时候可以自定义接受的关键字
      • def fun(a,b,c)
      • fun(c=1,a=2,b=3)
      • a,b,c = 2,3,1
  • 可变参数(VAR_POSITIONAL)
    • 未定义且不带关键字的参数传入,使用可变参数接受形成元组
      • def fun(*var)
      • fun(1,2,3)
      • var = (1,2,3)
  • 仅限关键字参数(KEYWORD_ONLY)
    • 可变参数后面的参数传入,只能带上关键字传入
      • def fun(*var,a)
      • fun(1,2,3,a=4)
      • var,a = (1,2,3),4
  • 可变关键字参数(VAR_KEYWORD)
    • 未定义的关键字参数传入,使用可变关键字参数接受形成字典
      • def fun(a,b,c,**kw)
      • fun(a=1,b=2,c=3,d=4,e=5)
      • a,b,c,kw = 1,2,3,{'d':4,'c':5}

参数信息保存在 __ code __. 通过 inspect.signature( func ).parameters.items() 可以看到函数的各项参数名称、类型、默认值

默认值保存在 __ defualt __. 通过 inspect.signature( func ).bind( param_dict ) 可以设定或改变参数的默认值

函数注解

def func(a:float, b:'int > 0' =80) -> str
  pass

Python 对注解所做的唯一的事情是,把它们存储在函数的 __ annotations __ 属性里。仅此而已,Python 不做检查、不做强制、 不做验证,什么操作都不做。

函数式编程

  • operator 模块
    • mul(a,b) 代替 lambda a,b: a*b
    • itemgetter(*idxs) 通过一系列特定位置提取序列中的相应元素
      sel = operator.itemgetter(1, 3)
      print(sel(range(1,10,2)))
      # (3,7)
      
    • attrgetter(*param) 通过一系列属性名称提取对象相应的属性值
  • functools 模块
    • reduce(func, sec) 在 sec 元素上进行 func 参数,与 filter和map 不同
      #阶乘
      def factorial(n):
        return functools.reduce(lambda a,b: a*b, range(1,n+1))
      

第6章 使用一等函数实现设计模式

  • 将外部函数作为参数传入对象的内部,以达到对象内部使用外部函数的目的

    def list_operater(l,func):
      for i in l:
          func(i,end=' ')
    list_operater(range(5),print)
    # 0 1 2 3 4
    
    promos = [operator.add, operator.mul, operator.sub]
    print(max(functools.reduce(promo, range(3,-2,-1)) for promo in promos))
    # 5
    

第7章 函数装饰器和闭包

装饰器

  • 装饰器是一个 “装饰” 函数的函数。
  • 装饰器在被装饰的函数定义之后立即进行运行
#----------- 1 ----------
@decorate
def target():
  print("target")

#----------- 2 ----------
def target():
  print("target")
target = decorate(target)

#----------上面两个等同-------------
def decorate(func):
  def inner():
    print("inner")
  return inner

target()
# 输出 inner
def dec(func):
    print("dec")
    return func
@dec
def func():
    print("func")
func()

# dec
# func

变量作用域

  • 在函数体内部有赋值的操作的变量默认为局部变量
  • global 适用于函数内部修改全局变量的值
  • nonlocal 适用于嵌套函数中内部函数修改外部变量的值
a = 6
def f2():
  print(a)

def f3():
  print(a)  # 这一行报错 需要定义global或nonlocal
  a = 5

f2()
f3()

闭包

  • 和使用匿名函数的方式一样,函数内部定义函数,且引用了*变量。
def fun():
    times = 0
    def add(t):
        nonlocal times
        times += t
        return times
    return add

cli = fun()
cli_1 = fun()
print(cli(10))  # 10
print(cli(20))  # 30
print(cli_1(7)) # 7
print(cli(10))  # 40
print(cli_1(9)) # 16
标准库中的装饰器
  • @functools.wraps( func )
    • 将 func 的属性复制到被装饰的函数中
  • @functools.lru_cache(maxsize=128, typed=False)
    • 备忘,将耗时的函数结果保存起来,避免传入相同参数时重复计算
    • maxsize 参数指定存储多少个调用结果,旧的丢掉。(应设置为2的幂值)
    • typed 当True时,不同参数类型得到的结果分开存储
单分派泛函数
  • 根据第一个参数的类型,以不同的方式执行相同操作的一组函数
  • @functools.singleispatch
@functools.singledispatch
def judgetype(obj):
    return 'obj'

#装饰器 [basefuncion].register([type]) 的形式
#函数名称无所谓了,用 _ 简单点
@judgetype.register(str)
def _(text):
    return 'str: '+repr(text)

@judgetype.register(numbers.Integral)
def _(n):
    return 'int: '+repr(n)

@judgetype.register(tuple)
def _(t):
    return 'tuple: '+repr(t)

@judgetype.register(abc.MutableSequence)
def _(seq):
    return 'seq: '+repr(seq)

print(judgetype(100))
print(judgetype('100'))
print(judgetype((100)))
print(judgetype([100]))

# int: 100
# str: '100'
# int: 100
# seq: [100]

叠放装饰器
  • 将 @d1 和 @d2 顺序应用到 f 函数上,作用相当于 f = d1(d2(f))
参数化装饰器(工厂函数)
  • def deco(a)
  • @deco(a=True) # 以函数的形式调用装饰器