命名元组中的类型名称的相关性

46
from collections import namedtuple

Point = namedtuple('whatsmypurpose',['x','y'])
p = Point(11,22)
print(p)

输出:

whatsmypurpose(x=11,y=22)

'whatsmypurpose'的相关性/用途是什么?


2
请查看此处:https://dev59.com/53A85IYBdhLWcg3wCe9Z。 "它变得更易读" - 因此,可读性是关键。而且,"您应该在任何您认为对象符号将使您的代码更加 Pythonic 和更易于阅读的地方使用命名元组而不是元组。" - Raul Guiu
3个回答

12

namedtuple() 是一个用于创建 tuple 子类的工厂函数。这里,'whatsmypurpose' 是类型名称。当您创建一个命名元组时,就会在内部创建一个具有此名称(whatsmypurpose)的类。

您可以通过使用 verbose 参数来注意到这一点,例如:

Point=namedtuple('whatsmypurpose',['x','y'], verbose=True)

你也可以尝试使用 type(p) 来验证这一点。


3.3 版本还添加了属性 _source,以便在需要打印或执行定义时使用。 - Eryk Sun
我似乎无法访问该类。也就是说,上面的 whatsmypurpose() 没有被定义。这个类是否真的被定义在某个地方,还是 typename 只是用于显示目的? - Gadi
@Gadi Point是上面示例中称为“whatsmypurpose”的类的引用(变量)。因此,通常类名称(Point.__name__)用于显示目的(尝试print(Point(1, 2)))。 - zvyn
1
我认为这里要强调的一个重要点是,Python不能通过查看您将类对象分配给的变量的名称来猜测您的新类名应该是什么。该变量尚不存在!因此,Python需要您明确告诉它要分配新类对象的名称。 - Christian Dean
或许可以通过消除显式分配“namedtuple(...)”到变量的需要,从而从另一端中删除冗余。例如,我可以想象类似于register_namedtuple('name', ..., locals())的东西,它会自动将name添加到locals()中。 - jamesdlin
True @jamesdlin,虽然我不确定函数在幕后如何侵入本地变量,但明确比隐含更好,不是吗?(https://www.python.org/dev/peps/pep-0020/)。 - Christian Dean

9

'whatsmypurpose' 确定了新的子类类型名称。来自文档:

collections.namedtuple(typename, field_names, verbose=False,rename=False)
返回一个名为 typename 的新元组子类。

以下是示例:

>>> from collections import namedtuple
>>> Foo = namedtuple('Foo', ['a', 'b'])
>>> type(Foo)
<class 'type'>
>>> a = Foo(a = 1, b = 2)
>>> a
Foo(a=1, b=2)
>>> Foo = namedtuple('whatsmypurpose', ['a', 'b'])
>>> a = Foo(a = 1, b = 2)
>>> a
whatsmypurpose(a=1, b=2)
>>> 

verbose参数设置为True,您可以查看完整的whatsmypurpose类定义。
>>> Foo = namedtuple('whatsmypurpose', ['a', 'b'], verbose=True)
from builtins import property as _property, tuple as _tuple
from operator import itemgetter as _itemgetter
from collections import OrderedDict

class whatsmypurpose(tuple):
    'whatsmypurpose(a, b)'

    __slots__ = ()

    _fields = ('a', 'b')

    def __new__(_cls, a, b):
        'Create new instance of whatsmypurpose(a, b)'
        return _tuple.__new__(_cls, (a, b))

    @classmethod
    def _make(cls, iterable, new=tuple.__new__, len=len):
        'Make a new whatsmypurpose object from a sequence or iterable'
        result = new(cls, iterable)
        if len(result) != 2:
            raise TypeError('Expected 2 arguments, got %d' % len(result))
        return result

    def _replace(_self, **kwds):
        'Return a new whatsmypurpose object replacing specified fields with new values'
        result = _self._make(map(kwds.pop, ('a', 'b'), _self))
        if kwds:
            raise ValueError('Got unexpected field names: %r' % list(kwds))
        return result

    def __repr__(self):
        'Return a nicely formatted representation string'
        return self.__class__.__name__ + '(a=%r, b=%r)' % self

    def _asdict(self):
        'Return a new OrderedDict which maps field names to their values.'
        return OrderedDict(zip(self._fields, self))

    def __getnewargs__(self):
        'Return self as a plain tuple.  Used by copy and pickle.'
        return tuple(self)

    a = _property(_itemgetter(0), doc='Alias for field number 0')

    b = _property(_itemgetter(1), doc='Alias for field number 1')

4
考虑以下内容:
class MyClass(tuple):
   pass

这个代码创建了一个类型,它是元组的子类,并且有一个名字MyClass.__name__ == "MyClass"namedtuple是一个类型工厂,它也创建元组的子类,但在这个函数式API中,你必须显式地传递名称。 当你将返回的类型分配给不同的名称时:
Point = namedtuple('whatsmypurpose',['x','y'])

这类似于这样做:
class whatsmypurpose(tuple):
    ... # extra stuff here to setup slots, field names, etc

Point = whatsmypurpose
del whatsmypurpose

在这两种情况下,您只是将不同的名称别名到类型。
通常,您会分配给与类型名称相同的名称。如果您担心重复使用相同的字符串不符合DRY原则,则可以使用typing.NamedTuple中的声明式API,而不是collections中的函数式API。 然后您可能需要注释类型。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接