Python3学习笔记 | 二十一、Python的函数-函数的高级话题
- 2019 年 10 月 6 日
- 笔记
部分设备阅读本文会存在代码错乱的情况,可点击阅读原文链接到博客中进行查看
一、函数设计概念
当我们使用函数时,就开始面对如何将组件组合在一起的选择。例如,如何将任务分解成为更有针对性的函数(导致了聚合性),函数将如何通讯(耦合性)等。我们要深入考虑函数的大小概念,因为它们直接影响到代码的可用性。 耦合性:对于输入使用参数并且对于输出使用return语句。 耦合性:只有真正必要的情况下使用全局变量。 耦合性:不要改变可变类型的参数,除非调用者希望这么做。 聚合性:每一个函数都应该有一个单一的、统一的目标。 大小:每一个函数应该相对较小。 耦合性:避免直接改变在另一个模块文件中的变量。
二、递归函数
之前笔记也提到过,就是调用自身来进行循环的函数。
>>> sum([1,2,3,4]) 10 >>> def mysum(l): ... if not l: ... return 0 ... else: ... return l[0]+mysum(l[1:]) ... >>> mysum([1,2,3,4,5]) 15
函数也可简单写成如下:
>>> def mysum(l): ... return 0 if not l else l[0] + mysum(l[1:]) ...
循环VS递归
一般情况下,循环会比递归简单。上面函数可以使用循环来解决,不需要递归,如下面的例子。
>>> sum = 0 >>> l = [1,2,3,4,5] >>> for i in l: ... sum += i ... >>> sum 15
但在一些特殊情况,递归还是比较有用。比如,获取下面列表中所有数字的合:
>>> l = [1,2,[3,[4,5],6],[7,[8,[9]]],10] >>> def sumtree(l): ... sum = 0 ... for i in l: ... if not isinstance(i,list): ... sum += i ... else: ... sum += sumtree(i) ... return sum ... >>> sumtree(l) 55
三、函数对象:属性和注解
在Python里函数也是以对象的形态出现。函数名也是以变量名形式存放。因此函数也可以跨模块,以参数形势等传递。函数对象也能调用根本无关的操作:属性存储与注释。
间接函数调用:
>>> def myprint(x): ... print(x) ... >>> myprint2 = myprint >>> myprint2('Dora') Dora
因为函数对象可以被引用到变量,因此函数里也可以以参数方式传递:
>>> def myfunc(func,text): ... func(text) ... >>> myfunc(myprint,'Dora') Dora
或者把函数放进列表或元组里:
>>> printing = [(myprint,'first'),(myprint,'second'),(myprint,'thrid')] >>> for func,text in printing: ... func(text) ... first second thrid
函数也可作为返回值:
>>> def make(food): ... def myprint(quantity): ... print('We made{}:{}'.format(food,quantity)) ... return myprint ... >>> Cake = make('cake') >>> Cake(10) We madecake:10
由于函数是对象,我们可以使用对象工具来处理函数。
>>> def myfunc(text): ... print(text) ... >>> dir(myfunc) ['__annotations__', '__call__', '__class__', '__closure__', '__code__', '__defaults__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__get__', '__getattribute__', '__globals__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__kwdefaults__', '__le__', '__lt__', '__module__', '__name__', '__ne__', '__new__', '__qualname__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__']
我们可以发现它有很多属性、方法等。
>>> myfunc.__name__ 'myfunc'
我们也可以查看代码里的内容:
>>> myfunc.__code__ <code object myfunc at 0x000002395FF8C5D0, file "<stdin>", line 1> >>> dir(myfunc.__code__) ['__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'co_argcount', 'co_cellvars', 'co_code', 'co_consts', 'co_filename', 'co_firstlineno', 'co_flags', 'co_freevars', 'co_kwonlyargcount', 'co_lnotab', 'co_name', 'co_names', 'co_nlocals', 'co_stacksize', 'co_varnames'] >>> myfunc.__code__.co_varnames ('text',) >>> myfunc.__code__.co_argcount 1
我们也可以在函数里添加任意属性,可以看到下面最后多了一个‘a’:
>>> myfunc.a = 10 >>> dir(myfunc) ['__annotations__', '__call__', '__class__', '__closure__', '__code__', '__defaults__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__get__', '__getattribute__', '__globals__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__kwdefaults__', '__le__', '__lt__', '__module__', '__name__', '__ne__', '__new__', '__qualname__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'a']
四、Python3.x中的函数注解
从Python3.0开始可以为函数的参数与返回值进行注解:
>>> def myfunc(a:int,b:str): ... return a + b ... >>> myfunc(1,3) 4 >>> def myfunc(a:int,b:str) -> list: ... return a + b ... >>> myfunc(1,3) 4 >>> myfunc.__annotations__ {'a': <class 'int'>, 'b': <class 'str'>, 'return': <class 'list'>}
但在代码里,没有任何限制。我们可以在特定工具可以利用。
五、匿名函数:lambda
之前也使用过lambda匿名函数。lambda会生成函数对象,但不赋值给任何变量。 lambda表达式:lambda [] [,] [,<arg3]…. : expression using args 参数不是必须的,但没有参数就没有相对意义。 lambda简单说明: lambda是一个表达式,而不是一个语句。--生成一个对象。lambda的主体是一个单个的表达式,而不是一个代码块。
>>> lambda a:a*a <function <lambda> at 0x00000239602732F0> >>> myfunc = lambda a:a*a >>> myfunc(3) 9 >>> (lambda a:a*a)(4) 16
为什么要使用 lambda?
通常 lambda 起到了一种函数速写的作用。功能上def完全可以代替lambda。但当我们把函数对象放进列表里等操作的时候,使用def感觉很臃肿。这个时候我们可以使用lambda来简化过程。
>>> funclist = [lambda x : x**2, ... lambda x : x**3, ... lambda x : x**4] >>> funclist[0](3) 9 >>> funclist[1](3) 27 >>> funclist[2](3) 81
六、lambda 作用域
对于lambda来说,作用域与函数相当。也遵循LEGB原则,关于LEGB原则可以参考:https://www.jianshu.com/p/3b72ba5a209c
>>> a = (lambda:1,print(2)) 2 >>> def action(x): ... return lambda y:x+y ... >>> act =action(10) >>> act(10) 20 >>> act = lambda x:lambda y :x+y >>> act_rslt = act(20) >>> act_rslt(10) 30 >>> (lambda x:lambda y:x+y)(20)(2) 22
七、在序列中映射函数:map
我们有个要求:下面列表里的每个值增加10 [1, 3, 5] 这个时候我们会想到循环
>>> l = [1,3,5] >>> for i in range(3): ... l[i] += 10 ... >>> l [21, 23, 25]
但这个要是使用map,会更简单。
>>> l = [1,3,5] >>> l = list(map(lambda x: x +10,l)) >>> l [11, 13, 15]
map的第一个传递参数是函数,第二个是可迭代的对象,每个对象当做函数的输入,输出结合为可迭代的对象(Python2.x里是列表)
八、函数式编程工具(1):filter
filter与map相似,但是针对返回的bool结果判断,结果为真,保留元素;结果为假,弃用元素。结果也是保存在可迭代的对象里,在Python2.x是存放列表里。
>>> list(filter((lambda x : x > 1),[-1,-3,-5,1,3,5])) [3, 5]
下面的示例因为返回的是列表里的值,只有0的时候bool值判断为假,因此除了0以外都保存了起来。
>>> list(filter((lambda x : x ),[-1,-3,-5,0,1,3,5])) [-1, -3, -5, 1, 3, 5]
九、函数式编程工具(2):reduce
reduce函数是在functools里的,因此我们得import这个函数。
>>> from functools import reduce >>> reduce((lambda x, y: x + y), [1, 2, 3, 4]) 10
这个方法是,第一次从可迭代对象里提取两个元素当做函数的参数传入,按前面的函数进行运算,保存返回值,当可迭代对象里还有元素的时候,之前的返回值为第一个参数,可迭代对象里取下一个继续运算,直到可迭代对象空。最后返回函数的返回值。
>>> reduce((lambda x, y: x + y), 'test text') 'test text' >>> reduce((lambda x, y: x + y), ['test', ' ', 'text']) 'test text'