Mec*_*Pig 4 python namedtuple python-typing
Python 3.6之后,我们有了typing.NamedTuple,它是 的类型化版本collections.namedtuple(),我们可以像类一样继承它:
class Employee(NamedTuple):
name: str
id: int
Run Code Online (Sandbox Code Playgroud)
相比之下collections.namedtuple,这个语法更漂亮,但是我还是看不懂它的实现,无论是看typing.py文件,还是做一些简单的测试,我们都会发现它是一个函数而不是一个类:
# Python 3.10.6 typing.py
def NamedTuple(typename, fields=None, /, **kwargs):
"""..."""
if fields is None:
fields = kwargs.items()
elif kwargs:
raise TypeError("Either list of fields or keywords"
" can be provided to NamedTuple, not both")
try:
module = sys._getframe(1).f_globals.get('__name__', '__main__')
except (AttributeError, ValueError):
module = None
return _make_nmtuple(typename, fields, module=module)
Run Code Online (Sandbox Code Playgroud)
>>> type(NamedTuple)
<class 'function'>
Run Code Online (Sandbox Code Playgroud)
我知道它使用了一些元类魔法,但我不明白使用时会发生什么class MyClass(NamedTuple)。为此,我尝试自定义一个函数来继承:
>>> def func_for_inherit(*args, **kwargs):
... print(args, kwargs)
...
>>> class Foo(func_for_inherit):
... foo: str
...
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: function() argument 'code' must be code, not str
Run Code Online (Sandbox Code Playgroud)
好吧,这得到了我无法理解的结果。当继承一个用户定义的函数时,似乎调用了它的类。这背后到底发生了什么?
typing.NamedTuple使用了一个非常深奥的功能__mro_entries__:
如果类定义中出现的基类不是 的实例
type,则__mro_entries__在其上搜索方法。如果找到,则使用原始基元组调用它。此方法必须返回将用来代替此基类的类的元组。元组可能为空,在这种情况下,原始基数将被忽略。
在函数定义之后,立即NamedTuple出现以下代码:
_NamedTuple = type.__new__(NamedTupleMeta, 'NamedTuple', (), {})
def _namedtuple_mro_entries(bases):
if len(bases) > 1:
raise TypeError("Multiple inheritance with NamedTuple is not supported")
assert bases[0] is NamedTuple
return (_NamedTuple,)
NamedTuple.__mro_entries__ = _namedtuple_mro_entries
Run Code Online (Sandbox Code Playgroud)
这设置NamedTuple.__mro_entries__为一个函数,告诉类创建系统使用实际的类 ,_NamedTuple作为基类。(_NamedTuple然后使用元类特性进一步定制类创建过程,最终结果是直接继承自 的类tuple。)
| 归档时间: |
|
| 查看次数: |
686 次 |
| 最近记录: |