python学习笔记(五)难点是闭包过一阵再看一遍

返回函数

不仅可以返回多个 值 ,还可以返回 函数
例:

def lazy_sum(*args)     #参数是未知个数的
    def  sum()          #在函数里面定义求和函数
        ax = 0          #初值 为0
        for n in args:  #把agss的数遍历一遍
            ax = ax +  n     #所有 数 相加
        return ax       #返回求和的值
    return  sum         #返回求和函数

调用:

f =  lazy_sum(1, 3, 5, 7, 9)
h = f()
print(h)

调用时返回的不是结果 而是一个函数,还得再调用一次才能 产生结果。
在lazy_sum中 又定义sum,内部函数sum就可以引用 外部函数lazy _sum的参数和局部变量。当lazy_sum返回函数sum 时,相关参数和变量都保存在返回的函数中,这种称为“闭包”的程序结构拥有极大的威力。
当我们调用lazy_sum()时,每次调用都会返回一个新的函数,即使传入相同的参数:

>>> f1 = lazy_sum(1, 3, 5, 7, 9)
>>> f2 = lazy_sum(1, 3, 5, 7, 9)
>>> f1==f2
False

并且f1()和f2()的调用结果互不影响。

闭包

lazy_sum返回的函数在其定义内部引用了局部变量 args,当一个函数返回了一个函数后,其内部的局部变量还被新函数引用,所以,闭包用起来简单,实现起来不容易。
另:返回的函数不会立即执行,直到调用了f()才执行
例:

def count():                #函数名
    fs = []                 #list
    for i in range(1, 4):   #循环3次
        def f():            #再定义一个函数
            return  i*i     #返回的 是i的乘积,注意这里 内部函数可以调用外部函数的变量
        fs.append(f)        #把f函数放到fs这个list中去
    return  fs              #返回结果集
#调用
f1, f2, f3 = count() 
>>> f1()
9
>>> f2()
9
>>> f3()
9

惊了,这三个的结果都是9而不是1,4,9,居然不是结果集。
原因是返回的函数引用了变量i,但它并非立即执行。等到3个函数都 返回时,它们所引用的变量i已经变成了 3,因此最终结果是 9。

  • 返回闭包时牢记一点:返回函数不要引用任何循环变量,或者后续会发生变化的变量。
    如果一定要引用循环变量,就再创建一个函数,用该函数的参数绑定循环变量当前的值,这样无论循环变量如何更改 ,已绑定到函数参数 的 值 不变。
def count():
    def f(j):               #用以绑定循环变量
        def g():
            return j*j
        return g
    fs  =  []               #结果集 list
    for i in range(1, 4):
        fs.append(f(i))     #绑定着循环变量添加
    return fs
#调用
f1, f2, f3 = count()
h = f1()
y = f2()
z  =  f3()
print(h)
print(y, z)

结果
结果
利用闭包返回一个计数器函数,每次调用它返回递增整数:

#对
def createCounter():
n = 0
    def counter():
        nonlocal n
        n += 1
        return n
    return counter
#不对
n = 0
def createCounter():
    def counter():
        global n
        n += 1
        return n
    return counter
#不对
def createCounter():
    n = 0
    def counter(n):
        def g():
            return n 
        return g
    fs = []
    fs.append(counter(n))
    n += 1
    return fs
#第一点 global不是这么用的 global是说 上面已经有个n了 想强调一下这两个是同一个n
#第二 这个计数器并不是返回多个函数 所以说 不是模仿刚才的那个f1f2f3
counterA = createCounter()
print(counterA(), counterA(), counterA(), counterA(), counterA()) # 1 2 3 4 5
counterB = createCounter()
if [counterB(), counterB(), counterB(), counterB()] == [1, 2, 3, 4]:
    print('测试通过!')
else:
    print('测试失败!')

烦人,写的不对

匿名函数

在传入函数时,不显式 的定义函数,而是 直接传入匿名函数。
比如 关键字 lambda 冒号前是参数,冒号后是内容
例: list(map(lambda x: x * x, [1, 2, 3, 4, 5, 6, 7, 8, 9]))
匿名函数不用写return,也没有名字,不用担心函数名冲突,也可以把匿名 函数赋值给 一个变量,再利用 变量来调用函数。
例:

>>> f = lambda x: x * x
>>> f
<function <lambda> at 0x101c6ef28>
>>> f(5)
25

也可以把匿名函数作为返回值返回,比如:

def build(x, y):
    return lambda: x * x + y * y

装饰器

**在代码运行期间动态增加功能的方式称之为decorator装饰器。**接受函数作为参数,并返回一个函数
由于函数也是一个对象可以赋值给变量。

>>> def now():
...     print('2015-3-25')
...
>>> f = now
>>> f()
2015-3-25

这就起了别名,函数 对象有__name__属性,通过f.__name__可以得到函数本名。

比如要增强now()的功能,在函数调用前后自动打印日志,但又不希望修改 now()函数的定义。

def  log(func)      #参数是一个函数
    def  wrpapper(*args, **kw):     #参数是未知个变量的list和kv对
        print('call  %s():' % func.__name__) #输出func的函数名
        return func(*args, **kw)    #返回func的参数
    return wrapper          #返回内置函数

因为它是一个decorator,所以接受一个函数 作为参数,并 返回一个函数。我们要借助python的@语法,把decorator置于函数的定义处:

@log
def now():
	print('2015-3-25')

调用now()函数,不仅会运行now ()函数本身,还会在 运行now()函数前打印一行日志:

#调用
now()
#控制台
call now():
2015-3-25

把@log放到 now()函数的定义处就相当于在now执行前执行了 一句now = log(now)
由于log()是一个decorator,返回一个函数,所以原来的now()函数仍然存在,只是现在同名的now变量指向了新的函数,也就是log返回的那个函数wrapper() ,于是调用now将执行新函数wrapper()。
wrapper()函数的 参数定义是(*args,**kw),可以接受任意参数的调用。在wrapper()内 ,首先打印日志,在紧接着调用原始函数func(),注意这里 的 func是刚刚的参数变量,也就是now,等于又调用了 now。
如果decorator本身需要传入参数,那就需要编写一个返回decorator的高阶函数。
例:自定义log 的文本

def log(text):         #第一层,参数是文本
    def decorator(func):        #第二层,参数是
        def wrapper(*args, **kw):
            print('%s : %s()' % (text, func.__name__))       #输出自定义文本和函数的本名
            return func(*args, **kw)        #调用这个函数
        return wrapper
    return decorator
@log('execute')      #相当于now = log('execute')(now)
def now():
    print('2015-3-25')
now()
print(now.__name__)

结果:
在这里插入图片描述分析:首先执行log(‘execute’),返回的是decorator函数,再调用decorator ,参数func是now,返回wrapper函数,调用 wrapper函数执行输出语句,输出execute和now的__name__,在经过 decorator装饰之后的函数,它们的__name__已经 从原来 的‘now‘变成了‘wrapper’。
函数wrapper()的名字是‘wrapper’,函数now()的名字也是‘wrapper’了,这样有些依赖函数签名的代码就会出错,需要把原始函数的__name__等属性复制到wrapper()函数中。
不用写wrapper.__name__=func.__name__,用python的内置函数functools.wraps即可
故,完整的decorator装饰器如下

import functools
def log(func):
    @functools.wraps(func)   #相当于 wrapper = functools.wraps(wrapper) 把now的名字保留下来
    def wrapper(*args,**kw):
        print('call: %s' % func.__name__)
        return func(*args, **kw)
    return wrapper
@log
def now():
    print('2020.6.28')
now()
print(now.__name__)

结果:
在这里插入图片描述
只用了一句@functools.wraps(func) 就让now的名字复制到函数内了
或者是带参的装饰器也是一样的

import  functools
def log(text):
    def decorator(func):
        @functools.wraps(func)
        def wrapper(*args, **kw):
            print(' %s %s():' % (text, func.__name__))
            return func(*args, **kw)
        return wrapper
    return  decorator

@log('execute')
def now():
    print('2020.6.28')
now()
print(now.__name__)

结果:
在这里插入图片描述
请设计一个decorator,它可作用于任何函数上,并打印该函数的执行时间:

#乱找的 测试没有通过  但是也能实现功能
def metric(fn):
    def decorator(*args, **kw):
        t1 = time.time()
        a =  fn(*args, **kw)
        t2 = time.time()
        print('%s executed in %s ms' % (fn.__name__, t2 - t1))
        return a
    return decorator

偏函数

python的functools模块提供了 很多有用的功能 ,其中一个就是偏函数partial function。
函数可以通过设定参数的默认值,可以降低函数调用的难度,偏函数也同样可以。
例:int()把字符串转换成十进制,但还提供额外的base参数,默认值为10。要是设定为其他值,就做那个值进制的转换。

>>> int('12345', base=8)
5349
>>> int('12345', 16)
74565

而在大量转换特定进制时很麻烦,可以设置一个函数来简化。
例:

def int2(x, base=2):
    return int(x, base)

functools.partial的作用就是 把一个函数的某些参数给固定住(也就是设置默认值),返回一个新的函数,调用这个新函数会更简单。
例:int2 = functools.partial(int, base=2)
相当于def int2(x, base=2): return int(x, base),也相当于上面那个函数。
创建偏函数时可以接收函数对象、*args**kw这三个参数。

当函数的参数个数太多,需要简化时,使用functools.partial可以创建一个新的函数,这个新函数可以固定住原函数的部分参数,从而在调用时更简单。

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值