函数(前面已经看过了,这次把博客补上,书中讲的内容并不难)
用def语句定义的函数是所有程序的基石。书中介绍默认参数、可接受任意数量参数的函数、关键字参数、参数注解以及闭包。
7.1 编写可接收任意数量参数的函数
问题:
编写一个可接收任意数量参数的函数
解决方案:
*args,**kwargs
def avg(first, *args): # args 返回的是元祖 return (first + sum(args)) / (1 + len(args)) import html def make_element(name, value, **kwargs): # kwargs返回的是字典 keyvals = [' %s="%s"' % item for item in kwargs.items()] attr_str = ''.join(keyvals) element = '<{name} {attrs}>{value}</{name}>'.format( name=name, attrs = attr_str, # 替换value中的<>符号 value = html.escape(value) ) return element if __name__ == '__main__': print(avg(12, 3, 4, )) print(avg(12, 3, 2, 32, 34, 3)) print(make_element('item', 'Albatross', size='large', quantity=6)) print(make_element('p', '<spam>'))
/usr/local/bin/python3.7 /Users/shijianzhong/study/PythonCookbook/chapter_7/t1_2.py 6.333333333333333 14.333333333333334 <item size="large" quantity="6">Albatross</item> <p ><spam></p> Process finished with exit code 0
讨论:
在函数定义中,以*打头的参数只能作为最后一个位置参数出现,而以**打头的参数只能作为最后一个参数出现。
不要犯初级的错误了,默认参数的位置,一定要在非默认参数的后面,今天这个小问题尽然卡住我了,丢人丢到家了。
7.2 编写只接收关键字参数的函数
问题
只通过关键字形式接收特定的参数
解决方案
# 传入的参数中,无论多少,第一个maxsize接收第一个参数,后面所有的位置参数全部*接收 # 想给*后面的参数赋值,必须通过关键形式. def recv(maxsize, *, block): ... def minium(*values, clip=None): m = min(values) # 如果默认不是空 if clip is not None: m = clip if clip < m else m return m if __name__ == '__main__': print(minium(1, 2, 3, 4, clip=5)) print(recv(1024, block=True)) print(recv(1024, True))
讨论
这个叫kewword-only参数常常是一种提高代码可读性的好方法。
7.3 将元数据信息附加到函数上
讨论:
在函数上面添加一些元信息
解决方案:
def add(x:int, y:int) -> int: return x+y if __name__ == '__main__': print(help(add)) # 通过annotations属性输出信息 print(add.__annotations__)
讨论:
一般没什么用,我也基本不会用,可以在验证参数的时候用一下,但装饰器更好用。
7.4 从函数中返回多个值
问题:
我们想从函数中返回多个值
解决方案:
返回一个元祖
def myfun(x, y, z): # 元祖的定义靠逗号 return x, y, z if __name__ == '__main__': res = myfun(1, 2, 3) print(res) print(type(res))
/usr/local/bin/python3.7 /Users/shijianzhong/study/PythonCookbook/chapter_7/t3_4.py (1, 2, 3) <class 'tuple'>
讨论:
没啥讨论的
7.5 定义带有默认参数的函数
问题
定义一个函数或者方法,其中由一个或者多个参数是可选的,并且带有默认值
解决方案:
# 普通默认参数 def spam1(a, b=42): print(a, b) # 定义参数为可变容器的话 def spam2(a, b=None): if b is None: b = [] _no_value = object() # 验证参数b有没有填写, 可以赋值一个object实例 def spam3(a, b=_no_value): if b is _no_value: print('No b value supplied') if __name__ == '__main__': spam3(1)
讨论:
定义带有默认参数的函数看似容易,但其实并不像看到的那么简单,很有道理。
流畅的Python书中说过,Python的函数传参其实使共享传参,在函数定义好以后,所有的函数参数就好比函数的属性,绑定在函数上面。
In [21]: x = 42 In [22]: def spam(a, b=x): ...: print(a,b) ...: In [23]: spam(1) 1 42 In [24]: x = 50 In [25]: spam(1) 1 42 In [26]: import inspect In [31]: inspect.getfullargspec(spam) Out[31]: FullArgSpec(args=['a', 'b'], varargs=None, varkw=None, defaults=(42,), kwonlyargs=[], kwonlydefaults=None, annotations={})
这里x属于不可变参数,定义函数的时候,b也指向了42值,当修改x的时候,并不影响42的值,所以函数的内部属性不会变。
In [32]: def spam(a, b=[]): ...: print(b) ...: return b ...: ...: In [33]: x = spam(1) [] In [34]: x Out[34]: [] In [35]: x.append(99) In [36]: x Out[36]: [99] In [37]: spam(1) [99] Out[37]: [99] In [38]:
上面一个定义了可变参数为函数属性的例子,这样使非常不可取的,只要脚本执行中,这个函数无论什么时候执行,这个可变参数都会根据函数的执行而发生变化,所有的函数执行都将共享这个可变参数,这是不对的。
def spam2(a, b=None):
if b not b:
b = []
在这个函数里这样定义b是否为None是不对的,因为'',[],空数据not以后都会变True
最后的那个ocject()默认值来测试是否提供了参数,还是比较有意思的,object()作为Python中所有对象的基类而存在,这玩意唯一的用处就是检测相等性。
7.6 定义匿名或内联函数
问题:
需要为高阶函数比如(sort)操作,但不想写def
解决方法:
lambda函数
In [44]: add = lambda x, y: x+y In [45]: add(2,3) Out[45]: 5 In [46]: add([1,2],[3,4]) Out[46]: [1, 2, 3, 4] In [47]: name = ['sidian','wudian','liusidian'] In [48]: sorted(name,key=lambda x: x[0]) Out[48]: ['liusidian', 'sidian', 'wudian'] In [49]:
讨论:
lambda不写复杂的逻辑
7.7 在匿名函数中绑定变量的值
问题:
我们利用lambda表达式定义了一个匿名函数,但是希望可以在函数定义的时候完成对特定变量的绑定。
解决方案:
这个还是蛮有意思的,匿名函数在冒号前面的参数,可以直接读取外部的全局变量参数,当匿名函数执行的时候,全部变量是什么他就读取什么。
这个跟定义好的函数,函数内部读取外部参数一样的逻辑。
In [49]: x = 10 In [50]: a = lambda y: x+y In [51]: x= 20 In [52]: b = lambda y: x+y In [53]: a(30) Out[53]: 50 In [54]: b(30) Out[54]: 50 In [55]:
没有绑定的情况,如果想绑定,可以填写默认参数一样。
In [55]: x = 10 In [56]: b = lambda y,x=x: x+y In [57]: x= 20 In [58]: a = lambda y,x=x: x+y In [59]: a(20) Out[59]: 40 In [60]: b(20) Out[60]: 30 In [61]:
讨论:
这个还是蛮有意思的,lambda函数还是非常好用的,但流畅的Python里面好像不是很推荐,不知道为什么。
In [61]: func = [lambda x: x+n for n in range(5)] In [62]: for f in func: ...: print(f(0)) ...: 4 4 4 4 4
这个是在执行时,读取变量n,那时候n就是4
In [66]: func = [lambda x,n=n: x+n for n in range(5)] In [67]: for f in func: ...: print(f(0)) ...: 0 1 2 3 4
这个是在定义的匿名函数的时候设置参数
7.8 让带有N个参数的可调用对象以较少的参数形式调用
问题
我们有一个可调用的对象可能会以回调函数的形式同其他的Python代码交互。但是这个可调用对象的参数过多,如果直接调用的话会产生异常。
解决方案:
用funtools.partail,但我感觉其实lambda就够了,我的感觉,lambda更加好理解,两个返回的都是函数,但lambda学精了,感觉太强大了