Python基本的内容已经回顾完了。这次的function真的是让我好好消化了一下。特别是Python中的闭包(Closure),由于之前没有实际用过,不是很能切身体会其用处。我目前只能理解到enclosing function这个层面。
function coding:
Python最简单的表示函数的方法是 def ,之后的code block就是一个函数体。当然还有lambda函数等等。Python不仅有一般的返回方式,比如 return ,还支持 yield (比较高级,到generator时再展开)。这两者的主要区别是yield记住了函数处理的位置。比如读一个大文件,使用yield就能一段段返回,这也是Python内置open函数的标准做法。
Python中还有compile能生成函数。最酷炫的定义函数的方法在下文提到。原文见stackoverflow.
Python中所有变量都是对象。函数也不例外。最简单的证明就是
1 2 3 4 5 6 | def f1(): print("false") f1() f2 = f1 f2() |
函数这个变量不是像C一样编译时就确定的。是运行时动态确定的。这点也很容易理解,毕竟Python是脚本语言。
1 2 3 4 5 6 7 8 9 10 11 12 13 | t = 1 if t: def f1(): print("true") def f3(): print("haha") else: def f1(): print("false") f1() f2 = f1 f2() |
function object:
既然函数是个对象,那么这里面装的是什么?自然的好奇,一下子将我带入高级领域了。接触了dis,code,inspect等等好多底层的,一下子反应不过来。先看最相关code。进行测试:
1 2 3 4 5 6 7 | print(type(f2)) # type: function print(dir(f2)) # __code__ is fun cf = f2.__code__ exec(cf) # run the function print(type(cf)) # type: code print(dir(cf)) # code attr print(cf.co_code) # the byte code |
输出为:
1 2 3 4 5 6 | <class 'function'> ['__annotations__', '__call__', '__class__', '__closure__', '__code__', '__defaults__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__get__', '__getattribute__', '__globals__', '__gt__', '__hash__', '__init__', '__kwdefaults__', '__le__', '__lt__', '__module__', '__name__', '__ne__', '__new__', '__qualname__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__'] true <class 'code'> ['__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__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'] b't\x00\x00d\x01\x00\x83\x01\x00\x01d\x02\x00d\x03\x00\x84\x00\x00}\x00\x00d\x00\x00S' |
code这个对象就是function的核心。我这么认为。因为这包含了函数的机器码。Python内置了反编译dis模块。看看酷炫的函数生成方式和dis模块的相应作用。再次声明这代码我目前没能力写出,理解也好半天,娱乐一下,原文见大神。输出见不看了,代码在Python 3可直接运行看输出。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | from types import CodeType import dis MyCode= CodeType( 0, 0, 0, 3, 64, bytes([101, 0, 0, #Load print function 101, 1, 0, #Load name 'a' 101, 2, 0, #Load name 'b' 23, #Take first two stack elements and store their sum 131, 1, 0, #Call first element in the stack with one positional argument 1, #Pop top of stack 101, 0, 0, #Load print function 101, 1, 0, #Load name 'a' 101, 2, 0, #Load name 'b' 20, #Take first two stack elements and store their product 131, 1, 0, #Call first element in the stack with one positional argument 1, #Pop top of stack 100, 0, 0, #Load constant None 83]), #Return top of stack (None,), ('print', 'a', 'b'), (), 'PersonalCodeObject', 'MyCode', 1, bytes([14,1]), (), () ) a=2 b=3 exec(MyCode) # code prints the sum and the product of "a" and "b" print(MyCode.co_code) print(dis.dis(MyCode)) |
nested function:
Python的作用是根据LEGB来的。从Local->enclosing->global->buildin。最难以理解的是enclosing。而且Python2和3还有区别。Python3多了nonlocal,一下子提升了nested function的实用性,或者说是提供了便利性。先看代码例子。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | def tester(start): state = start def nested(label): nonlocal state print(label, state) state += 1 return nested F = tester(0) F("a") F("b") F("c") G = tester(100) G("a") G("b") G("c") |
nested function的主要应用场景就是上面所示。生成一个附带上下文信息的独立函数。比如F,G各自都是独立的带有信息state。这貌似就是class。但实际上还是有区别的。首先写法就不一样。然后结构也不一样。然后据说效率也不一样。当然同样的功能class一定能实现。
回到LEGB。如上面的例子。E就是代码nested function到最外层的函数为止的作用域,不包括最外层的函数外面的。在Python 2只能在nested function里引用E中变量,即read only。Python 3使用nonlocal后就能表明这个变量要在nested function里改变。虽然Python 2能用一些技巧实现同样的事,但总归不自然。
函数还有部分内容,内容太多,分开写。
链接: