我试图将一个较长的中空“数据”类转换为命名元组。我的类目前看起来是这样的:
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)的出现,但这不是我喜欢的。
那么,是否存在一个好技巧,可以让我的重写成功,而不增加大量的代码复杂性(元编程),或者我应该吞下药丸,继续“搜索和替换”?:)
结合@Denis和@Mark的方法:
from collections import namedtuple
import inspect
class Node(namedtuple('Node', 'left right val')):
__slots__ = ()
def __new__(cls, *args, **kwargs):
args_list = inspect.getargspec(super(Node, cls).__new__).args[len(args)+1:]
params = {key: kwargs.get(key) for key in args_list + kwargs.keys()}
return super(Node, cls).__new__(cls, *args, **params)
这应该支持使用位置参数和混合情况创建元组。
测试用例:
>>> print Node()
Node(left=None, right=None, val=None)
>>> print Node(1,2,3)
Node(left=1, right=2, val=3)
>>> print Node(1, right=2)
Node(left=1, right=2, val=None)
>>> print Node(1, right=2, val=100)
Node(left=1, right=2, val=100)
>>> print Node(left=1, right=2, val=100)
Node(left=1, right=2, val=100)
>>> print Node(left=1, right=2)
Node(left=1, right=2, val=None)
但也支持TypeError:
>>> Node(1, left=2)
TypeError: __new__() got multiple values for keyword argument 'left'
使用我的高级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。
短的,简单的,不会导致人们不恰当地使用isinstance:
class Node(namedtuple('Node', ('val', 'left', 'right'))):
@classmethod
def make(cls, val, left=None, right=None):
return cls(val, left, right)
# Example
x = Node.make(3)
x._replace(right=Node.make(4))
这是一个直接来自文档的例子:
默认值可以通过使用_replace()来实现
原型实例:
>>> Account = namedtuple('Account', 'owner balance transaction_count')
>>> default_account =帐户('<所有者名称>',0.0,0)
>>> johns_account = default_account._replace(owner='John')
>>> janes_account = default_account._replace(owner='Jane')
所以,OP的例子是:
from collections import namedtuple
Node = namedtuple('Node', 'val left right')
default_node = Node(None, None, None)
example = default_node._replace(val="whut")
然而,我更喜欢这里给出的其他一些答案。为了完整起见,我想加上这个。
我不确定是否有一个简单的方法,只有内置的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)