我试图将一个较长的中空“数据”类转换为命名元组。我的类目前看起来是这样的:

class Node(object):
    def __init__(self, val, left=None, right=None):
        self.val = val
        self.left = left
        self.right = right

转换为namedtuple后,它看起来像:

from collections import namedtuple
Node = namedtuple('Node', 'val left right')

但这里有一个问题。我最初的类允许我只传入一个值,并通过为named/keyword参数使用默认值来处理默认值。喜欢的东西:

class BinaryTree(object):
    def __init__(self, val):
        self.root = Node(val)

但这在重构的命名tuple中不起作用,因为它期望我传递所有字段。我当然可以替换Node(val)到Node(val, None, None)的出现,但这不是我喜欢的。

那么,是否存在一个好技巧,可以让我的重写成功,而不增加大量的代码复杂性(元编程),或者我应该吞下药丸,继续“搜索和替换”?:)


当前回答

使用我的高级Enum (aenum)库中的NamedTuple类,并使用类语法,这是相当简单的:

from aenum import NamedTuple

class Node(NamedTuple):
    val = 0
    left = 1, 'previous Node', None
    right = 2, 'next Node', None

一个潜在的缺点是,任何具有默认值的属性都需要__doc__字符串(对于简单属性是可选的)。在实际使用中是这样的:

>>> Node()
Traceback (most recent call last):
  ...
TypeError: values not provided for field(s): val

>>> Node(3)
Node(val=3, left=None, right=None)

这比justinfay的答案更有优势:

from collections import namedtuple

class Node(namedtuple('Node', ['value', 'left', 'right'])):
    __slots__ = ()
    def __new__(cls, value, left=None, right=None):
        return super(Node, cls).__new__(cls, value, left, right)

是简单,以及是基于元类而不是基于exec。

其他回答

jterrace给出的使用recordtype的答案很好,但是库的作者建议使用他的namedlist项目,该项目同时提供了可变(namedlist)和不可变(namedtuple)实现。

from namedlist import namedtuple
>>> Node = namedtuple('Node', ['val', ('left', None), ('right', None)])
>>> Node(3)
Node(val=3, left=None, right=None)
>>> Node(3, 'L')
Node(val=3, left=L, right=None)

我不确定是否有一个简单的方法,只有内置的namedtuple。有一个很好的模块叫做recordtype,它有这个功能:

>>> from recordtype import recordtype
>>> Node = recordtype('Node', [('val', None), ('left', None), ('right', None)])
>>> Node(3)
Node(val=3, left=None, right=None)
>>> Node(3, 'L')
Node(val=3, left=L, right=None)

将其包装在函数中。

NodeT = namedtuple('Node', 'val left right')

def Node(val, left=None, right=None):
  return NodeT(val, left, right)

由于您正在使用namedtuple作为数据类,您应该注意到python 3.7将为此目的引入@dataclass装饰器——当然它有默认值。

文档中的一个例子:

@dataclass
class C:
    a: int       # 'a' has no default value
    b: int = 0   # assign a default value for 'b'

比破解namedtuple更干净,可读和可用。不难预测,随着3.7的采用,namedtuples的使用将会减少。

如果你想保留使用类型注释的可能性,不幸的是,@mark-lodato非常好的解决方案是不可用的(它失败于我设置__defaults__)。 另一种方法是使用attrs:

import attr

 
@attr.s
class Node(object):
    val: str = attr.ib()
    left: 'Node' = attr.ib(None)
    right: 'Node' = attr.ib(None)

这有:

类型注解 漂亮的__str__和__repr__ 可定制的,因为它是一个真正的类 所有Python版本的实现都相同