本文主要是介绍《Python 源码剖析》一些理解以及勘误笔记(2),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
以下是本人阅读此书时理解的一些笔记,包含一些影响文义的笔误修正,当然不一定正确,贴出来一起讨论。
注:此书剖析的源码是2.5版本,在python.org 可以找到源码。纸质书阅读,pdf 贴图。
文章篇幅太长,故切分成3部分,这是第二部分。
p248: 嵌套函数、闭包和 decorator
co_cellvars: 通常是一个tuple,保存嵌套的作用域内使用的变量名集合;
co_freevars: 通常是一个tuple,保存使用了的外层作用域中的变量名集合。
如下的一段Python 代码:
1 2 3 4 5 6 7 8 | def get_func(): value = "inner" def inner_func(): print value return inner_func show_value = get_func() show_value() |
则py 文件编译出来的PyCodeObject 有3个,那么与get_func 对应的对象中的 co_cellvars 就应该包含字符串 "value",而与 inner_func
对应的PyCodeObject 对象的co_freevars 也应该有字符串"value"。
闭包从创建、传递到使用的全过程可以用以下三幅图演示:
inner_func 可以认为是 get_func 的局部变量,如图2 中 inner_func 对应的 PyFunctionObject 对象的 func_closure 指向 tuple。在inner_func 调用过
程中,tuple 中包含的一个个cell 对象就被放到 f_localplus 中相应的位置,当引用外层作用域符号时,一定是先到 f_localsplus 中的 free 变量区域获
取符号对应的值。实际上 value 的值可以通过 show_value.__closure__[0].cell_contents 访问到。使用闭包的时候需要注意返回的函数不要引用任何循环变量,或者后续会发生变化的变量,否则出现的情况可能与你预期不同。
在closure 技术的基础上,Python 实现了 decorator,decorator 可以认为是 "func = should_say(func)" 的一种包装形式。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | # decorator 实现 def should_say(fn): def say(*args): print 'say something...' fn(*args) return say @should_say def func(): print 'in func' func() # 输出结果为 # say something... # in func # 不用decorator 的实现 ... def func(): print 'in func' func = should_say(func) func() |
注意还有含参的装饰器(再封装一层),以及装饰类(接收一个类,并返回一个新类)。
p264: Python 中的可调用性(callable)。只要一个对象对应的class 对象中实现了"__call__" 操作(更确切地说,在 Python 内部的
PyTypeObject 中,tp_call 不为空),那么这个对象就是一个可调用的对象,比如:
class A(object):
def __call__(self): print 'Hello Python'
那么 a= A() a() 会输出'Hello Python' ;可以认为 PyA_Type 对象的 tp_call 不为空。在 c++ 看来也就是函数对象的实现。
所谓 “调用”,就是执行对象的 type 所对应的 class 对象的 tp_call 操作。
p268: 内置类型对应的PyTypeObject 的tp_dict 填充、descriptor
在Python 内部,存在多种 descriptor,PyType_Ready 在通过add_operators 添加了 PyTypeObject 对象中定义的一些 operator 后,
还会通过 add_methods、add_members、add_getsets 添加在PyType_Object 中定义的 tp_methods、tp_members、tp_getset 函数
集。这些 add_*** 的过程与 add_operator 类似,不过最后添加到 tp_dict 中的descriptor 就不再是PyWrapperDescrObject,而分别是
PyMethodDescrObject、PyMemberDescrObject、PyGetSetDescrObject。
注:PyWrapperDescrObject 的 ob_type 是 PyWrapperDescr_Type,PyWrapperDescr_Type 对象中的 tp_call 是wrapperdescr_call,当
Python 虚拟机”调用“一个 descriptor 时,也就会调用 wrapperdescr_call 。
一般而言,对于一个 Python 中的对象obj,如果 obj.__class__ 对应的 class 对象中存在 __get__ 、__set__、__delete__ 三种操作,那么 obj 可以称
为Python 的一个 descriptor。像 PyWrapperDescr_Type 的 tp_descr_get 设置了 wrapperdescr_get,故称 PyWrapperDescrObject 为 descriptor。
如上图来说,实际上 mp_subscript 和 d_wrapped 都是函数指针变量,它们的值相等,都是 list_subscript 。
如下的例子重写了list 的 '__repr__ ' 方法,则初始化完成后的 A 如下图所示:
class A(list):
def __repr__(self): return ‘Python'
即如果没有重写则 A.tp_repr 没有定义,A.tp_dict 里面也没有定义 '__repr__',当 a = A(); a.__repr__() 找到是在mro 列表中某个基类定义的
'__repr__' ,比如 PyList_Type 的 d_wrapped 和 tp_repr 一样,都是 list_repr。
如果重写了则创建时A.tp_repr 被赋值为 slot_to_repr。在 slot_to_repr 中,会寻找 '__repr__' 方法对应的 PyFunctionObject 对象,正好就找到在 A 定
义中重写的函数。比如 A.__dict__['__repr__'] 显示是<function __repr__ at ...>,而
这篇关于《Python 源码剖析》一些理解以及勘误笔记(2)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!