Created
June 4, 2017 09:06
-
-
Save ruoyu0088/405d1c09f948c4dd1072bd57ca467f05 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
"cells": [ | |
{ | |
"cell_type": "code", | |
"execution_count": null, | |
"metadata": { | |
"collapsed": true | |
}, | |
"outputs": [], | |
"source": [ | |
"# super" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"## 改变描述器的搜索途径" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"`super`是一个类型,其对应的C语言结构体为`PySuper_Type`。其主要目的是调用父类中定义的方法。创建`super`对象需要两个参数:`type_`和`obj`,其中`type_`是一个类型对象,`obj`对象是`type_`类型的一个实例。当访问`super`对象的属性时,将在`type_.__mro__`中位于`type(obj)`之后的类型对象中搜索描述器(Descriptor)。其实现代码在`super_getattro()`中。下面通过一个例子解释`super`的属性搜索行为:" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 76, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"class A:\n", | |
" def func(self):\n", | |
" print(\"A.func\", self.name)\n", | |
" \n", | |
"class B(A):\n", | |
" def __init__(self, name):\n", | |
" self.name = name\n", | |
" \n", | |
" def func(self):\n", | |
" print(\"B.func\", self.name)" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"`b.func`将首先在`b`的类型`B`的`__dict__`中搜索`\"func\"`,由于`B.__dict__[\"func\"]`是一个函数对象,函数对象具有`__get__`属性,因此`b.func`得到的是一个`bound method`对象,该对象对`b`和`B.__dict__[\"func\"]`包装,调用它时相当与调用`B.__dict__[\"func\"](b)`。这就是Python实现方法调用的方式。" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 21, | |
"metadata": {}, | |
"outputs": [ | |
{ | |
"name": "stdout", | |
"output_type": "stream", | |
"text": [ | |
"<bound method B.func of <__main__.B object at 0x000000E562CED208>>\n", | |
"B.func b\n" | |
] | |
} | |
], | |
"source": [ | |
"b = B(\"b\")\n", | |
"print(b.func)\n", | |
"b.func()" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"下面来看`super`是如何改变属性的搜索路径的。`B.__mro__`中有三个元素,其中`A`和`object`位于`B`之后,因此获取`super`对象`sb`的`func`属性时,将依次检查`A`和`object`的`__dict__`中是否有名为`func`的描述器,由于在`A`中找到了,因此将得到调用它的`__get__()`所得到的`bound method`对象。" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 22, | |
"metadata": {}, | |
"outputs": [ | |
{ | |
"name": "stdout", | |
"output_type": "stream", | |
"text": [ | |
"(<class '__main__.B'>, <class '__main__.A'>, <class 'object'>)\n", | |
"A.func b\n" | |
] | |
} | |
], | |
"source": [ | |
"sb = super(B, b)\n", | |
"print(B.__mro__)\n", | |
"\n", | |
"sb.func()" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"下面分别查看`b.func`和`sb.func`这两个`bound method`对象中`__func__`属性的函数全名:" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 29, | |
"metadata": {}, | |
"outputs": [ | |
{ | |
"name": "stdout", | |
"output_type": "stream", | |
"text": [ | |
"B.func A.func\n" | |
] | |
} | |
], | |
"source": [ | |
"print(b.func.__func__.__qualname__, sb.func.__func__.__qualname__)" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"## Python3中的super" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"`super`通常用于调用父类的方法,然而在Python 2中由于使用`super`使需要传递类对象和实例对象,使得调用`super`的语句不简洁,并且一旦需要修改类名,所有的`super`调用都需要修改。在Python 3中这一点得到了改进,例如下面的程序中在`B.func`中使用`super`时,不需要传递任何参数。" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 36, | |
"metadata": {}, | |
"outputs": [ | |
{ | |
"name": "stdout", | |
"output_type": "stream", | |
"text": [ | |
"A.func b\n", | |
"B.func b\n" | |
] | |
} | |
], | |
"source": [ | |
"class A:\n", | |
" def func(self):\n", | |
" print(\"A.func\", self.name)\n", | |
" \n", | |
"class B(A):\n", | |
" def __init__(self, name):\n", | |
" self.name = name\n", | |
" \n", | |
" def func(self):\n", | |
" super().func()\n", | |
" print(\"B.func\", self.name)\n", | |
" \n", | |
"b = B(\"b\")\n", | |
"b.func()" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"下面我们分析`super()`是如何得到`B`和`self`这两个对象的。" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"### 获取`self`参数" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"在类中定义的函数,其第一个参数一定是实例对象,因此只需要找到第一个参数所指向的对象即可。Python中每层函数调用都会创建对应的`frame`(与`PyFrameObject`结构体对应)对象。其最后一个字段`f_localsplus`是可变长的数组:\n", | |
"\n", | |
"```c\n", | |
"PyObject *f_localsplus[1]; /* locals+stack, dynamically sized */\n", | |
"```\n", | |
"\n", | |
"这个数组中的头一个元素就是传递给与其对应的函数的第一个参数。因此可以在`super`对象的初始化函数`super_init()`中找到这样的语句:\n", | |
"\n", | |
"```c\n", | |
"if (type == NULL) {\n", | |
" /* Call super(), without args -- fill in from __class__\n", | |
" and first local variable on the stack. */\n", | |
" PyFrameObject *f;\n", | |
" PyCodeObject *co;\n", | |
" Py_ssize_t i, n;\n", | |
" f = PyThreadState_GET()->frame;\n", | |
" /* ... */\n", | |
" obj = f->f_localsplus[0];\n", | |
"}\n", | |
"```\n", | |
"\n", | |
"即若没有传递类型对象,则把当前的`frame`对象的`f_localsplus`数组中的第一个元素作为实例对象。" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"下面通过程序验证上述事实。`get_frame_localsplus(frame, idx)`用于获取`frame`中`f_localsplus`字段的第`idx`元素。关于其具体实现,请读者参考`frame`相关的章节。" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 56, | |
"metadata": { | |
"collapsed": true | |
}, | |
"outputs": [], | |
"source": [ | |
"import ctypes\n", | |
"import inspect\n", | |
"\n", | |
"def get_frame_localsplus(frame, idx):\n", | |
" code = frame.f_code\n", | |
" extras = code.co_nlocals + len(code.co_cellvars) + len(code.co_freevars) + code.co_stacksize\n", | |
" addr = id(frame) + frame.__sizeof__() - (extras - idx) * ctypes.sizeof(ctypes.c_ssize_t)\n", | |
" return ctypes.cast(addr, ctypes.POINTER(ctypes.c_ssize_t))[0]\n", | |
"\n", | |
"class A:\n", | |
" def first_locals_is_self(self):\n", | |
" frame = inspect.currentframe()\n", | |
" first_ref = get_frame_localsplus(frame, 0)\n", | |
" return first_ref" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"在`A.first_locals_is_self`中,首先通过`inspect.currentframe()`获得当前运行中的`frame`对象,然后调用`get_frame_localsplus()`获得其中`f_localsplus`字段的第0个元素。下面验证这第0个元素就是实例`a`的地址:" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 57, | |
"metadata": {}, | |
"outputs": [ | |
{ | |
"name": "stdout", | |
"output_type": "stream", | |
"text": [ | |
"985205208064 985205208064\n" | |
] | |
} | |
], | |
"source": [ | |
"a = A()\n", | |
"print(id(a), a.first_locals_is_self())" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"### 获取当前的类" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"为了在`super_init()`中获取包含正在被调用的函数的类,Python 3对编译器进行了一些改动。为了理解这些改动,我们先看看类是如何被创建的。当Python编译器遇到`class`关键字定义类时,将在一个新的运行环境(字典)中运行`class`中的代码。然后调用`type()`创建类型对象。下面是与`class`关键字等效的代码。" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 55, | |
"metadata": {}, | |
"outputs": [ | |
{ | |
"name": "stdout", | |
"output_type": "stream", | |
"text": [ | |
"<__main__.A object at 0x000000E562D04668>\n" | |
] | |
} | |
], | |
"source": [ | |
"env = {}\n", | |
"\n", | |
"exec(\"\"\"\n", | |
"def func(self):\n", | |
" print(self)\n", | |
"\"\"\", env)\n", | |
"\n", | |
"A = type(\"A\", (object,), env)\n", | |
"\n", | |
"a = A()\n", | |
"a.func()" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"由上述的过程可知,当类中的方法函数定义时,类型对象还未被创建,因此Python 3在类型对象创建之后,对其中包含`super`的函数进行处理。为这些函数创建`__closure__`属性,在其中使用`__class__`引用该函数所属的类。\n", | |
"\n", | |
"下面看一个例子:" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 78, | |
"metadata": { | |
"collapsed": true | |
}, | |
"outputs": [], | |
"source": [ | |
"class A:\n", | |
" \n", | |
" def func_with_super(self):\n", | |
" super\n", | |
" \n", | |
" def func_no_super(self):\n", | |
" pass" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"`A.func_with_super()`中引用了`super`符号,因此Python编译器会在创建类型`A`时,为该函数添加一个名为`__class__`的`freevar`变量,该变量引用`__closure__[0].cell_contents`,即类型对象`A`:" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 79, | |
"metadata": {}, | |
"outputs": [ | |
{ | |
"name": "stdout", | |
"output_type": "stream", | |
"text": [ | |
"('__class__',)\n", | |
"<class '__main__.A'>\n" | |
] | |
} | |
], | |
"source": [ | |
"print(A.func_with_super.__code__.co_freevars)\n", | |
"print(A.func_with_super.__closure__[0].cell_contents)" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"在`A.func_no_super()`中没有引用`super`符号,因此其`__closure__`为空:" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 82, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"A.func_no_super.__closure__" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"注意Python编译器只检查函数中是否有`super`的引用,而不关心它实际内容是什么。在下面的例子中`func_with_mysuper`的`__closure__`为空,而`func_with_super`的`__closure__`则有类型`B`的引用。" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 87, | |
"metadata": {}, | |
"outputs": [ | |
{ | |
"name": "stdout", | |
"output_type": "stream", | |
"text": [ | |
"None\n", | |
"(<cell at 0x000000E562C5DF18: type object at 0x000000E566222AA8>,)\n", | |
"(<cell at 0x000000E562C5DF18: type object at 0x000000E566222AA8>,)\n" | |
] | |
} | |
], | |
"source": [ | |
"mysuper, super = super, \"something\"\n", | |
"\n", | |
"class B:\n", | |
" def func_with_mysuper(self):\n", | |
" mysuper\n", | |
" \n", | |
" def func_with_super(self):\n", | |
" super\n", | |
" \n", | |
" def func_with_super2(self):\n", | |
" super = 2\n", | |
" super\n", | |
" \n", | |
"print(B.func_with_mysuper.__closure__) \n", | |
"print(B.func_with_super.__closure__)\n", | |
"print(B.func_with_super2.__closure__) \n", | |
"super = mysuper" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"相关的代码:\n", | |
"\n", | |
"* `symtable.c`中`case Name_kind:`部分检查是否存在`super`的引用。如果存在则添加一个`__class__`引用。\n", | |
"* `compile.c`中`if (u->u_ste->ste_needs_class_closure) {`的部分检查函数是否存在`__class__`引用,如果存在,则产生相应的代码。\n", | |
"* 创建类时会调用内置函数`__build_class__()`执行创建类的代码。\n", | |
"* 在创建类的代码中可以找到创建`__class__`的语句。" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 1, | |
"metadata": {}, | |
"outputs": [ | |
{ | |
"name": "stdout", | |
"output_type": "stream", | |
"text": [ | |
" 2 0 LOAD_BUILD_CLASS\n", | |
" 1 LOAD_CONST 1 (<code object A at 0x000000B9468434B0, file \"<ipython-input-1-794cc6fd2a02>\", line 2>)\n", | |
" 4 LOAD_CONST 2 ('A')\n", | |
" 7 MAKE_FUNCTION 0\n", | |
" 10 LOAD_CONST 2 ('A')\n", | |
" 13 CALL_FUNCTION 2 (2 positional, 0 keyword pair)\n", | |
" 16 STORE_FAST 0 (A)\n", | |
"\n", | |
" 5 19 LOAD_FAST 0 (A)\n", | |
" 22 RETURN_VALUE\n" | |
] | |
} | |
], | |
"source": [ | |
"def test():\n", | |
" class A:\n", | |
" def func(self):\n", | |
" super\n", | |
" return A\n", | |
"\n", | |
"import dis\n", | |
"dis.dis(test)" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"上面的代码动态地创建了一个函数,并且把它作为参数传递给了`__build_class__()`函数。这个被创建的函数的代码如下:" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 3, | |
"metadata": {}, | |
"outputs": [ | |
{ | |
"name": "stdout", | |
"output_type": "stream", | |
"text": [ | |
" 2 0 LOAD_NAME 0 (__name__)\n", | |
" 3 STORE_NAME 1 (__module__)\n", | |
" 6 LOAD_CONST 0 ('test.<locals>.A')\n", | |
" 9 STORE_NAME 2 (__qualname__)\n", | |
"\n", | |
" 3 12 LOAD_CLOSURE 0 (__class__)\n", | |
" 15 BUILD_TUPLE 1\n", | |
" 18 LOAD_CONST 1 (<code object func at 0x000000B946879270, file \"<ipython-input-1-794cc6fd2a02>\", line 3>)\n", | |
" 21 LOAD_CONST 2 ('test.<locals>.A.func')\n", | |
" 24 MAKE_CLOSURE 0\n", | |
" 27 STORE_NAME 3 (func)\n", | |
" 30 LOAD_CLOSURE 0 (__class__)\n", | |
" 33 RETURN_VALUE\n" | |
] | |
} | |
], | |
"source": [ | |
"class_code = test.__code__.co_consts[1]\n", | |
"dis.dis(class_code)" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"可以看到函数的返回值是一个`cell`对象。在`__build_class__`的代码中,如果`cell`变量通过`PyCell_Check(cell)`检查,则调用`PyCell_Set(cell, cls)`将该`cell`的内容设置为被创建的类:\n", | |
"\n", | |
"```c\n", | |
"cell = PyEval_EvalCodeEx(PyFunction_GET_CODE(func), PyFunction_GET_GLOBALS(func), ns,\n", | |
" NULL, 0, NULL, 0, NULL, 0, NULL,\n", | |
" PyFunction_GET_CLOSURE(func));\n", | |
"if (cell != NULL) {\n", | |
" PyObject *margs;\n", | |
" margs = PyTuple_Pack(3, name, bases, ns);\n", | |
" if (margs != NULL) {\n", | |
" cls = PyEval_CallObjectWithKeywords(meta, margs, mkw);\n", | |
" Py_DECREF(margs);\n", | |
" }\n", | |
" if (cls != NULL && PyCell_Check(cell))\n", | |
" PyCell_Set(cell, cls);\n", | |
" Py_DECREF(cell);\n", | |
"}\n", | |
"```" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": null, | |
"metadata": { | |
"collapsed": true | |
}, | |
"outputs": [], | |
"source": [] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 84, | |
"metadata": { | |
"collapsed": true | |
}, | |
"outputs": [], | |
"source": [ | |
"B.func_with_super.__closure__" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 195, | |
"metadata": { | |
"collapsed": true | |
}, | |
"outputs": [], | |
"source": [ | |
"import sys\n", | |
"import dis\n", | |
"import inspect\n", | |
"import ctypes" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 329, | |
"metadata": { | |
"collapsed": true | |
}, | |
"outputs": [], | |
"source": [ | |
"def get_object(addr):\n", | |
" ctypes.cast(addr, ctypes.py_object).value\n", | |
"\n", | |
"def get_frame_localsplus(frame):\n", | |
" code = frame.f_code\n", | |
" extras = code.co_nlocals + len(code.co_cellvars) + len(code.co_freevars) + code.co_stacksize\n", | |
" addr_localsplus = id(frame) + frame.__sizeof__() - extras * ctypes.sizeof(ctypes.c_ssize_t)\n", | |
" arr_localsplus = (ctypes.c_ssize_t * extras).from_address(addr_localsplus)\n", | |
" return list(arr_localsplus)\n", | |
"\n", | |
"code_attrs = \"co_argcount co_stacksize co_nlocals co_cellvars co_freevars co_varnames\"\n", | |
"\n", | |
"def local_counts(code):\n", | |
" for attr in code_attrs.split():\n", | |
" print(attr, getattr(code, attr))" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 279, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"class A:\n", | |
" def first_locals_is_self(self):\n", | |
" frame = inspect.currentframe()\n", | |
" addrs = get_frame_localsplus(frame)\n", | |
" return addrs\n", | |
" \n", | |
" def call_super(self):\n", | |
" super()\n", | |
" \n", | |
" def no_super(self):\n", | |
" pass" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 280, | |
"metadata": {}, | |
"outputs": [ | |
{ | |
"name": "stdout", | |
"output_type": "stream", | |
"text": [ | |
"149438360\n", | |
"[149438360, 121862680, 0, 149344600, 121862680]\n" | |
] | |
} | |
], | |
"source": [ | |
"a = A()\n", | |
"print(id(a))\n", | |
"print(a.first_locals_is_self())" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 282, | |
"metadata": {}, | |
"outputs": [ | |
{ | |
"name": "stdout", | |
"output_type": "stream", | |
"text": [ | |
"co_argcount 1\n", | |
"co_stacksize 1\n", | |
"co_nlocals 1\n", | |
"co_cellvars ()\n", | |
"co_freevars ()\n", | |
"co_varnames ('self',)\n" | |
] | |
} | |
], | |
"source": [ | |
"local_counts(A.no_super.__code__)" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 283, | |
"metadata": {}, | |
"outputs": [ | |
{ | |
"name": "stdout", | |
"output_type": "stream", | |
"text": [ | |
"co_argcount 1\n", | |
"co_stacksize 1\n", | |
"co_nlocals 1\n", | |
"co_cellvars ()\n", | |
"co_freevars ('__class__',)\n", | |
"co_varnames ('self',)\n" | |
] | |
} | |
], | |
"source": [ | |
"local_counts(A.call_super.__code__)" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 286, | |
"metadata": {}, | |
"outputs": [ | |
{ | |
"data": { | |
"text/plain": [ | |
"__main__.A" | |
] | |
}, | |
"execution_count": 286, | |
"metadata": {}, | |
"output_type": "execute_result" | |
} | |
], | |
"source": [ | |
"A.call_super.__closure__[0].cell_contents" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 296, | |
"metadata": {}, | |
"outputs": [ | |
{ | |
"name": "stdout", | |
"output_type": "stream", | |
"text": [ | |
"('__class__',)\n", | |
"<cell at 0x0000000008E54888: empty>\n", | |
"<cell at 0x0000000008E54888: type object at 0x000000000724D138>\n", | |
"True\n" | |
] | |
} | |
], | |
"source": [ | |
"class B: \n", | |
" def call_super(self):\n", | |
" super()\n", | |
" \n", | |
" print(call_super.__code__.co_freevars)\n", | |
" print(call_super.__closure__[0])\n", | |
" \n", | |
"print(B.call_super.__closure__[0])\n", | |
"print(B.call_super.__closure__[0].cell_contents is B)" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 297, | |
"metadata": { | |
"collapsed": true | |
}, | |
"outputs": [], | |
"source": [ | |
"class C:\n", | |
" def has___class__(self):\n", | |
" print(__class__)" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 298, | |
"metadata": {}, | |
"outputs": [ | |
{ | |
"data": { | |
"text/plain": [ | |
"('__class__',)" | |
] | |
}, | |
"execution_count": 298, | |
"metadata": {}, | |
"output_type": "execute_result" | |
} | |
], | |
"source": [ | |
"C.has___class__.__code__.co_freevars" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 300, | |
"metadata": {}, | |
"outputs": [ | |
{ | |
"data": { | |
"text/plain": [ | |
"True" | |
] | |
}, | |
"execution_count": 300, | |
"metadata": {}, | |
"output_type": "execute_result" | |
} | |
], | |
"source": [ | |
"C.has___class__.__closure__[0].cell_contents is C" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 304, | |
"metadata": {}, | |
"outputs": [ | |
{ | |
"data": { | |
"text/plain": [ | |
"()" | |
] | |
}, | |
"execution_count": 304, | |
"metadata": {}, | |
"output_type": "execute_result" | |
} | |
], | |
"source": [ | |
"my_super = super\n", | |
"\n", | |
"class D:\n", | |
" def use_my_super(self):\n", | |
" return my_super()\n", | |
" \n", | |
"D.use_my_super.__code__.co_freevars" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 326, | |
"metadata": { | |
"collapsed": true | |
}, | |
"outputs": [], | |
"source": [ | |
"def outer_func(__class__):\n", | |
" \n", | |
" def inner_func1(self):\n", | |
" return super()\n", | |
" \n", | |
" def inner_func2(self):\n", | |
" __class__\n", | |
" return my_super()\n", | |
" \n", | |
" return inner_func1, inner_func2" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 328, | |
"metadata": {}, | |
"outputs": [ | |
{ | |
"name": "stdout", | |
"output_type": "stream", | |
"text": [ | |
"<super: <class 'D'>, <D object>>\n", | |
"<super: <class 'D'>, <D object>>\n" | |
] | |
} | |
], | |
"source": [ | |
"f1, f2 = outer_func(D)\n", | |
"d = D()\n", | |
"print(f1(d))\n", | |
"print(f2(d))" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 353, | |
"metadata": { | |
"collapsed": true | |
}, | |
"outputs": [], | |
"source": [ | |
"class E:\n", | |
" def nested_func_ng(self, a, b):\n", | |
" def func():\n", | |
" return super()\n", | |
" return func\n", | |
" \n", | |
" def nested_func_ok(self, a, b):\n", | |
" sup = super()\n", | |
" def func():\n", | |
" return sup\n", | |
" return func" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 354, | |
"metadata": {}, | |
"outputs": [ | |
{ | |
"ename": "RuntimeError", | |
"evalue": "super(): no arguments", | |
"output_type": "error", | |
"traceback": [ | |
"\u001b[1;31m---------------------------------------------------------------------------\u001b[0m", | |
"\u001b[1;31mRuntimeError\u001b[0m Traceback (most recent call last)", | |
"\u001b[1;32m<ipython-input-354-cd32dd100183>\u001b[0m in \u001b[0;36m<module>\u001b[1;34m()\u001b[0m\n\u001b[0;32m 1\u001b[0m \u001b[0me\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mE\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m----> 2\u001b[1;33m \u001b[0me\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mnested_func_ng\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;36m1\u001b[0m\u001b[1;33m,\u001b[0m \u001b[1;36m2\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m", | |
"\u001b[1;32m<ipython-input-353-75a6447c7031>\u001b[0m in \u001b[0;36mfunc\u001b[1;34m()\u001b[0m\n\u001b[0;32m 2\u001b[0m \u001b[1;32mdef\u001b[0m \u001b[0mnested_func_ng\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mself\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0ma\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mb\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 3\u001b[0m \u001b[1;32mdef\u001b[0m \u001b[0mfunc\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m----> 4\u001b[1;33m \u001b[1;32mreturn\u001b[0m \u001b[0msuper\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m 5\u001b[0m \u001b[1;32mreturn\u001b[0m \u001b[0mfunc\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 6\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n", | |
"\u001b[1;31mRuntimeError\u001b[0m: super(): no arguments" | |
] | |
} | |
], | |
"source": [ | |
"e = E()\n", | |
"e.nested_func_ng(1, 2)()" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 355, | |
"metadata": {}, | |
"outputs": [ | |
{ | |
"data": { | |
"text/plain": [ | |
"<super: __main__.E, <__main__.E at 0x8ea8da0>>" | |
] | |
}, | |
"execution_count": 355, | |
"metadata": {}, | |
"output_type": "execute_result" | |
} | |
], | |
"source": [ | |
"e.nested_func_ok(1, 2)()" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": null, | |
"metadata": { | |
"collapsed": true | |
}, | |
"outputs": [], | |
"source": [ | |
"import inspect\n", | |
"\n", | |
"def new_build_class(*arg, **kw):\n", | |
" global new_func\n", | |
" new_func = arg[0]\n", | |
" \n", | |
" def f():\n", | |
" global res, frame\n", | |
" frame = inspect.currentframe()\n", | |
" res = new_func()\n", | |
" return res\n", | |
" \n", | |
" tmp = list(arg)\n", | |
" tmp[0] = f\n", | |
" arg = tuple(tmp)\n", | |
" \n", | |
" res = old__build_class__(*arg, **kw)\n", | |
" return res\n", | |
"\n", | |
"__builtin__.__build_class__, old__build_class__ = new_build_class, __build_class__" | |
] | |
} | |
], | |
"metadata": { | |
"anaconda-cloud": {}, | |
"kernelspec": { | |
"display_name": "Python [default]", | |
"language": "python", | |
"name": "python3" | |
}, | |
"language_info": { | |
"codemirror_mode": { | |
"name": "ipython", | |
"version": 3 | |
}, | |
"file_extension": ".py", | |
"mimetype": "text/x-python", | |
"name": "python", | |
"nbconvert_exporter": "python", | |
"pygments_lexer": "ipython3", | |
"version": "3.5.2" | |
} | |
}, | |
"nbformat": 4, | |
"nbformat_minor": 1 | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment