国庆节快乐~~虽说今天是假期的最后一天。。
好久没学习Python了。。值得高兴的是 《怪物猎人:世界》目前所有的龙我(统枪)都打过一遍了 (/得意)。
正题,开始学习、记录:
返回函数
顾名思义,返回函数即 返回值为函数。调用一个函数,返回另一个函数,当执行另一个函数时,另一个函数的内部才执行。有点绕,示例如下:
>>> def test(alist):
... def sum():
... s = 0
... for x in alist:
... s = s + x
... return s
... return sum
...
>>> L = [1,3,5,7,9]
>>> c = test(L)
>>> c
<function test.<locals>.sum at 0x000001B48D574488>
>>> c()
25
每次调用这个函数都会生成一个新的函数,函数不相等,函数的结果相等,接着上面的示例,如下:
>>> c1 = test(L)
>>> c2 = test(L)
>>> c1 == c2
False
>>> c1() == c2()
True
学习到这里时,涉及到了一个新的概念,名为"闭包"。
我理解的 闭包 即 调用一个函数所返回的函数里包含参数及变量,返回的函数是一个待执行完成的函数,只要调用就会根据其内部的元素结构返回结果。
这样的话会产生一个问题,示例如下:
>>> def count():
... f = []
... for i in range(1, 4):
... def ft():
... return i*i
... f.append(ft)
... return f
...
>>> f = count()
>>> for x in f:
... x()
...
9
9
9
>>> f[0]
<function count.<locals>.f at 0x000001B48D5746A8>
上述示例中,首先看起来返回的是 列表f ,不是函数。实际上函数是存放在 列表f 中,返回的是个函数列表;其次,遍历执行列表中的函数,值相同。想要的结果 是返回 1、4、9 ,不是 9、9、9。
由于在函数中定义了 变量 i ,i 在不断循环获取新的值,所生成的函数并没有执行运算。当循环完成,在开始执行运算时,i指向的变量值为循环所得的最后一个值,即3,所以值都为9。
想要得到结果为 1、4、9,需要针对循环的值做下处理,示例如下:
>>> def count():
... def f(j):
... def g():
... return j*j
... return g
... fs = []
... for i in range(1, 4):
... fs.append(f(i))
... return fs
...
>>> f = count()
>>> for x in f:
... x()
...
1
4
9
返回函数在返回前执行了运算,将 i 当前的值传入了返回函数中。
练习题:利用闭包返回一个计数器函数,每次调用它返回递增整数。自解法如下:
>>> def createCounter():
... a=[0]
... def counter():
... a[0] += 1
... return a[0]
... return counter
...
>>> c = createCounter()
>>> c()
1
>>> c()
2
>>> c()
3
此题中还有其他解法,涉及到了变量范围问题,在这里把我理解的简单说下:
nonlocal
让内部函数获取并使用其外层函数定义的变量,以练习题示例如下:
>>> def createCounter():
... a = 0
... def counter():
... nonlocal a
... a += 1
... return a
... return counter
...
>>> c = createCounter()
>>> c()
1
>>> c()
2
global
内部想要修改全局变量时,需要在局部先声明全局变量,示例如下:
>>> a = 0
>>> def createCounter():
... def counter():
... global a
... a += 1
... return a
... return counter
...
>>> c = createCounter()
>>> c()
1
>>> c()
2
>>> c()
3
匿名函数
顾名思义,即没有名字的函数。这种函数有个好处,就是不用担心函数命名冲突的问题,使用方式如下所示:
>>> t = lambda x: x*x
>>> t
<function <lambda> at 0x000001B48D574488>
>>> t(10)
100
# lambda x: x*x 为匿名函数,它等于下方函数
>>> def ft(x):
... return x*x
关键字 lambda 表示为匿名函数,匿名函数有限制,只能有一个表达式,不需要写return ,返回值即表达式结果。
它的用法也很灵活,比如写在函数的返回值里等等。
装饰器
装饰器(Decorator),简单来说,就是在不改变原函数的情况下,动态加强函数的功能。
由于函数也是一个对象,而且函数对象可以赋值给变量,所以也能通过变量调用函数。
在Python的函数对象中,有 " __name__ " 属性(下划线前面两个,后面两个),调用它可以得到函数的名字,示例如下所示:
>>> def test():
... print('Hello world!')
...
>>> test.__name__
'test'
>>> a = test
>>> a.__name__
'test'
下面写一个装饰器(Decorator),实现在调用函数时打印一条语句。本质上,装饰器就是一个返回函数,示例如下:
>>> def log(func):
... def wrapper(*arg,**kw):
... print('call %s:' % func.__name__)
... return func(*arg,**kw)
... return wrapper
...
如上所示,log函数接收一个函数,也返回一个函数。使用它需要借助Python中的@语法,示例如下:
>>> @log
... def now():
... print('2018-10-07')
...
>>> now()
call now:
2018-10-07
如上所示,在要调用的函数上一行加 @和 装饰器名称,在调用函数时便自动打印出了一句事先写好的话。
此时,将此函数赋值给一个变量,打印变量的函数名,如下所示:
>>> tname = now
>>> tname.__name__
'wrapper'
其实,把装饰器放在函数定义上方,相当于执行了:
now = log(now)
执行 now函数前,Python生成一个同函数名的变量,并让它等于执行传入函数的装饰器后返回函数。看装饰器的代码部分,log函数先执行了wrapper函数,输出了一条语句,之后返回执行传入的函数;再之后返回一个wrapper函数。
如果装饰器需要传参的话,会比较复杂,但也是个返回函数,示例如下:
>>> def log(test):
... def decotator(func):
... def wrapper(*args,**kw):
... print('%s %s:' % (test,func.__name__))
... return func(*args,**kw)
... return wrapper
... return decotator
...
>>> @log('The test')
... def now():
... print('2018-10-07')
...
>>> now()
The test now:
2018-10-07
现在还有一个重要的问题,调用当前函数返回的函数名是 wrapper ,而不是当前函数名。上述代码中当前函数名等其他函数属性并未复制到 wrapper函数中,若代码中有函数依赖函数的签名,则会出错。Python中内置的 functools.wraps 可以解决这个问题,示例如下:
>>> import functools
>>> def log(func):
... @functools.wraps(func)
... def wrapper(*args,**kw):
... print('call %s:' % func.__name__)
... return func(*args,**kw)
... return wrapper
...
>>> @log
... def now():
... print('2018-10-07')
...
>>> now()
call now:
2018-10-07
>>> tnow = now
>>> tnow.__name__
'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('Good')
... def now():
... print('2018-10-07')
...
>>> now()
Good now
2018-10-07
>>> tnow = now
>>> tnow.__name__
'now'
回顾下知识点,%s为占位符号,详情在这里。
偏函数
简单来说,偏函数就是给函数设置默认值,使得后面写代码时调用函数不用总输入固定值,示例如下:
>>> int('123')
123
>>> int('123',base=8)
83
>>> def int2(x,base=2):
... return int(x,base)
...
>>> int2('1000000')
64
如上所示,这样调用函数就方便了。在Python中我们可以用functools.partial直接生成一个偏函数,不需要我们自己定义,示例如下:
>>> import functools
>>> int2 = functools.partial(int,base=2)
>>> int2('1000000')
64
本篇就到这里,教材网址:https://www.liaoxuefeng.com, 继续学习~~