Python 为什么';namedtuple模块是否使用元类来创建nt类对象?
几周前,我花了一些时间调查这件事。该模块使用一个工厂函数,将动态数据(新的Python 为什么';namedtuple模块是否使用元类来创建nt类对象?,python,metaclass,python-internals,namedtuple,Python,Metaclass,Python Internals,Namedtuple,几周前,我花了一些时间调查这件事。该模块使用一个工厂函数,将动态数据(新的namedtuple类的名称和类属性名称)填充到一个非常大的字符串中。然后执行exec,以字符串(表示代码)作为参数,并返回新类 有人知道为什么会这样做,当有一个特定的工具可以用于这类事情时,即元类?我自己没有尝试过这么做,但似乎namedtuple模块中发生的所有事情都可以使用namedtuple元类轻松完成,如下所示: class namedtuple(type): 等等。中有一些提示。作者提出了一种创建命名元组的新
namedtuple
类的名称和类属性名称)填充到一个非常大的字符串中。然后执行exec
,以字符串(表示代码)作为参数,并返回新类
有人知道为什么会这样做,当有一个特定的工具可以用于这类事情时,即元类?我自己没有尝试过这么做,但似乎namedtuple
模块中发生的所有事情都可以使用namedtuple
元类轻松完成,如下所示:
class namedtuple(type):
等等。中有一些提示。作者提出了一种创建命名元组的新方法,但遭到拒绝,并给出以下评论: 原始版本的好处似乎是速度更快, 由于硬编码的关键方法。 -安托万·皮特罗 使用exec并没有什么不好的地方。早期版本使用其他版本 方法和它们被证明是不必要的复杂,并且具有意想不到的效果 问题。命名元组的一个关键特性是它们完全相同 相当于手写的课堂雷蒙德·赫廷格 此外,以下是以下部分的说明: 。。。配方已经发展到目前的exec风格,我们可以从中获得所有 免费使用Python的高速内置参数检查。新的 构建和执行模板的样式使 __与此配方的以前版本相比,repr___;的功能更快、更干净 如果您正在寻找一些替代实现:
- Jan Kaliszewski的食谱
- 亚伦·艾利斯(见他的)
exec
的另一个理由是,出于安全原因,一些地方(阅读公司)禁用了它
除了高级的Enum
和NamedConstant
,*还有基于元类的NamedTuple
*aenum
由和backport的作者编写。这里是另一种方法
""" Subclass of tuple with named fields """
from operator import itemgetter
from inspect import signature
class MetaTuple(type):
""" metaclass for NamedTuple """
def __new__(mcs, name, bases, namespace):
cls = type.__new__(mcs, name, bases, namespace)
names = signature(cls._signature).parameters.keys()
for i, key in enumerate(names):
setattr(cls, key, property(itemgetter(i)))
return cls
class NamedTuple(tuple, metaclass=MetaTuple):
""" Subclass of tuple with named fields """
@staticmethod
def _signature():
" Override in subclass "
def __new__(cls, *args):
new = super().__new__(cls, *args)
if len(new) == len(signature(cls._signature).parameters):
return new
return new._signature(*new)
if __name__ == '__main__':
class Point(NamedTuple):
" Simple test "
@staticmethod
def _signature(x, y, z): # pylint: disable=arguments-differ
" Three coordinates "
print(Point((1, 2, 4)))
如果说这种方法有什么优点的话,那就是简单。如果不使用命名为tuple.\uuuu new\uuuuu
,它将更简单,但仅用于强制元素计数。如果没有这一点,它很高兴地允许额外的匿名元素超过命名元素,省略元素的主要效果是在按名称访问被省略的元素时,索引器
(只需少量工作,就可以转换为AttributeError
)。元素计数不正确的错误消息有点奇怪,但它可以理解这一点。我不希望它与Python2一起工作
还有进一步复杂化的空间,例如\uuuu repr\uuu
方法。我不知道性能与其他实现相比如何(缓存签名长度可能会有所帮助),但与本机namedtuple
实现相比,我更喜欢调用约定。还有一个原因,其他答案都没有提到*
一个类只能有一个元类。原因之一是元类充当创建类的工厂。随意把工厂混在一起是不可能的。您必须创建一个知道如何按正确顺序调用多个工厂的“组合工厂”,或者创建一个知道“父工厂”并正确使用它的“子工厂”
如果namedtuple
使用了自己的元类,则涉及任何其他元类的继承都会中断:
>>M1类(类型):。。。
...
>>>M2类(类型):。。。
...
>>>类C1(元类=M1):。。。
...
>>>C2类(元类=M2):。。。
...
>>>C类(C1、C2):。。。
...
回溯(最近一次呼叫最后一次):
文件“”,第1行,在
TypeError:元类冲突:派生类的元类必须是其所有基元类的(非严格)子类
相反,如果您想拥有自己的元类并从namedtuple
类继承,则必须使用某种所谓的namedtuple\u meta
元类来实现这一点:
从namedtuple导入namedtuple_meta#假装存在
类MyMeta(类型):。。。
类MyMetaWithNT(名称为tuple\u meta,MyMeta):。。。
类C(元类=MyMetaWithNT):。。。
..或者直接从namedtuple\u meta
继承自定义元类:
class MyMeta(namedtuple_meta): ...
class C(metaclass=MyMeta): ...
起初这看起来很容易,但是编写自己的mataclass并很好地处理某些(复杂的)nt元类可能很快就会出现问题。这种限制可能不会经常出现,但经常会阻碍namedtuple
的使用。因此,让所有命名的tuple
类都是类型
类型,并消除自定义元类的复杂性,这无疑是一个优势
*Raymond Hettinger的的确暗示了这一点:
命名元组的一个关键特性是它们与手写类完全等效
嗯,这当然回答了一般的问题,但我很想知道这些意想不到的问题在哪里。根据元类本身的不同,问题可能在于元类本身,在这种情况下,元类可能需要修复。2.5年后对该评论的回复似乎也提出了一些人们可能有的真实问题。无论如何,谢谢你的链接,那里有很多信息。我从来没有真正买过这个。在我看来,答案总是“因为雷蒙德·赫廷格(Raymond Hettinger)可以使用奇怪的黑客技术”。作为一个正在学习的人,这真的让我停下来在标准库中看到类似的东西。我曾认为标准库将是检验“好”的好地方