Python 命名元组的可变默认参数
Mutable default argument for a Python namedtuple
我发现了一种让命名元组使用来自 here 的默认参数的巧妙方法。
from collections import namedtuple
Node = namedtuple('Node', 'val left right')
Node.__new__.__defaults__ = (None, None, None)
Node()
Node(val=None, left=None, right=None)
如果您希望 'right' 的默认值为空列表,您会怎么做?您可能知道,使用可变的默认参数(例如列表)是不行的。
有没有简单的方法来实现这个?
你不能那样做,因为 __defaults__
中的值是实际的默认值。也就是说,如果你写了一个有 someargument=None
的函数,然后用 someargument = [] if someargument is None else someargument
或类似的东西检查函数体内,相应的 __defaults__
条目仍然是 None .换句话说,你可以用一个函数来做到这一点,因为在一个函数中你可以编写代码来做任何你想做的事情,但你不能在 namedtuple 中编写自定义代码。
但是如果你想要默认值,只需创建一个具有该逻辑的函数,然后创建正确的命名元组:
def makeNode(val=None, left=None, right=None):
if right is None:
val = []
return Node(val, left, right)
中给出的方法非常有效。我看到的唯一缺点是必须知道(在其他用户的情况下)和记住才能使用工厂函数而不是命名元组 class- 在创建对象时,以及在执行以下操作时:
isinstance(node, Node) # success
isinstance(node, makeNode) # misery
解决此问题的方法可能是执行如下所示的操作。
NodeBase = nt('NodeBase', 'val left right')
NodeBase.__new__.__defaults__ = (None, None, None)
class Node(NodeBase):
'''A namedtuple defined as:
Node(val, left, right)
with default values of (None, None, [])'''
__slots__ = ()
def __new__(cls, *args, **kwargs):
obj = super().__new__(cls, *args, **kwargs)
if obj.right is None:
obj = obj._replace(right = [])
return obj
Rick Teachey 对实现的小改动,默认值可以在 class:
之外设置
NodeBase = namedtuple('NodeBase', 'val left right')
class Node(NodeBase):
__slots__ = ()
def __new__(cls, *, right=[], **kwargs):
obj = super().__new__(cls, right=right, **kwargs)
return obj
#IMPLEMENTATION
kw = {'val': 1, 'left':12}
m = Node(**kw)
# outputs Node(val=1, left=12, right=[])
自从提出这个问题以来,dataclasses
模块已经被提议并接受到 Python 中。该模块与 namedtuples
有很多重叠的用例,但具有更多的灵活性和功能。特别是,当您想为可变字段指定默认值时,您可以指定一个工厂函数。
from typing import List
from dataclasses import dataclass, field
@dataclass
class Node:
val: str
left: List["Node"] = field(default_factory=list)
right: List["Node"] = field(default_factory=list)
在命名元组中指定各个字段的类型,因此在这种情况下我必须填写一些空白并假设 val
是一个字符串并且 left
和right
都是其他 Node
对象的列表。
由于 right
和 left
是 class 定义中赋值的左侧,因此当我们初始化 Node
对象时,它们是可选参数。此外,我们可以提供一个默认值,但我们提供了一个默认工厂,这是一个在我们初始化 Node
对象而不指定这些字段时使用 0 个参数调用的函数。
例如:
node_1 = Node('foo')
# Node(val='foo', left=[], right=[])
node_2 = Node('bar', left=[node_1])
# Node(val='bar', left=[Node(val='foo', left=[], right=[])], right=[])
node_3 = Node('baz')
# Node(val='baz', left=[], right=[])
node_4 = Node('quux', left=[node_2], right=[node_3])
# Node(val='quux', left=[Node(val='bar', left=[Node(val='foo', left=[], right=[])], right=[])], right=[Node(val='baz', left=[], right=[])])
就我个人而言,对于任何我需要的不仅仅是最薄的数据容器的应用程序,我发现自己的目标是 dataclasses
而不是 namedtuples
。
我发现了一种让命名元组使用来自 here 的默认参数的巧妙方法。
from collections import namedtuple
Node = namedtuple('Node', 'val left right')
Node.__new__.__defaults__ = (None, None, None)
Node()
Node(val=None, left=None, right=None)
如果您希望 'right' 的默认值为空列表,您会怎么做?您可能知道,使用可变的默认参数(例如列表)是不行的。
有没有简单的方法来实现这个?
你不能那样做,因为 __defaults__
中的值是实际的默认值。也就是说,如果你写了一个有 someargument=None
的函数,然后用 someargument = [] if someargument is None else someargument
或类似的东西检查函数体内,相应的 __defaults__
条目仍然是 None .换句话说,你可以用一个函数来做到这一点,因为在一个函数中你可以编写代码来做任何你想做的事情,但你不能在 namedtuple 中编写自定义代码。
但是如果你想要默认值,只需创建一个具有该逻辑的函数,然后创建正确的命名元组:
def makeNode(val=None, left=None, right=None):
if right is None:
val = []
return Node(val, left, right)
isinstance(node, Node) # success
isinstance(node, makeNode) # misery
解决此问题的方法可能是执行如下所示的操作。
NodeBase = nt('NodeBase', 'val left right')
NodeBase.__new__.__defaults__ = (None, None, None)
class Node(NodeBase):
'''A namedtuple defined as:
Node(val, left, right)
with default values of (None, None, [])'''
__slots__ = ()
def __new__(cls, *args, **kwargs):
obj = super().__new__(cls, *args, **kwargs)
if obj.right is None:
obj = obj._replace(right = [])
return obj
Rick Teachey 对实现的小改动,默认值可以在 class:
之外设置NodeBase = namedtuple('NodeBase', 'val left right')
class Node(NodeBase):
__slots__ = ()
def __new__(cls, *, right=[], **kwargs):
obj = super().__new__(cls, right=right, **kwargs)
return obj
#IMPLEMENTATION
kw = {'val': 1, 'left':12}
m = Node(**kw)
# outputs Node(val=1, left=12, right=[])
自从提出这个问题以来,dataclasses
模块已经被提议并接受到 Python 中。该模块与 namedtuples
有很多重叠的用例,但具有更多的灵活性和功能。特别是,当您想为可变字段指定默认值时,您可以指定一个工厂函数。
from typing import List
from dataclasses import dataclass, field
@dataclass
class Node:
val: str
left: List["Node"] = field(default_factory=list)
right: List["Node"] = field(default_factory=list)
在命名元组中指定各个字段的类型,因此在这种情况下我必须填写一些空白并假设 val
是一个字符串并且 left
和right
都是其他 Node
对象的列表。
由于 right
和 left
是 class 定义中赋值的左侧,因此当我们初始化 Node
对象时,它们是可选参数。此外,我们可以提供一个默认值,但我们提供了一个默认工厂,这是一个在我们初始化 Node
对象而不指定这些字段时使用 0 个参数调用的函数。
例如:
node_1 = Node('foo')
# Node(val='foo', left=[], right=[])
node_2 = Node('bar', left=[node_1])
# Node(val='bar', left=[Node(val='foo', left=[], right=[])], right=[])
node_3 = Node('baz')
# Node(val='baz', left=[], right=[])
node_4 = Node('quux', left=[node_2], right=[node_3])
# Node(val='quux', left=[Node(val='bar', left=[Node(val='foo', left=[], right=[])], right=[])], right=[Node(val='baz', left=[], right=[])])
就我个人而言,对于任何我需要的不仅仅是最薄的数据容器的应用程序,我发现自己的目标是 dataclasses
而不是 namedtuples
。