sas*_*uke 265 python default-value optional-arguments namedtuple
我正在尝试将一个冗长的空洞"数据"类转换为一个命名元组.我的班级目前看起来像这样:
class Node(object):
def __init__(self, val, left=None, right=None):
self.val = val
self.left = left
self.right = right
Run Code Online (Sandbox Code Playgroud)
转换为namedtuple它后看起来像:
from collections import namedtuple
Node = namedtuple('Node', 'val left right')
Run Code Online (Sandbox Code Playgroud)
但这里有一个问题.我的原始类允许我传入一个值,并使用命名/关键字参数的默认值来处理默认值.就像是:
class BinaryTree(object):
def __init__(self, val):
self.root = Node(val)
Run Code Online (Sandbox Code Playgroud)
但是这对我重构的名为元组的情况不起作用,因为它希望我传递所有字段.当然,我可以代替的出现Node(val)到Node(val, None, None),但它不是我的胃口.
所以确实存在着一个很好的技巧,它可以使我重新写成功无需添加大量的代码复杂度(元编程),或者我应该只吞下药丸,并与"查找和替换"继续前进?:)
Mar*_*ato 472
使用defaults参数.
>>> from collections import namedtuple
>>> fields = ('val', 'left', 'right')
>>> Node = namedtuple('Node', fields, defaults=(None,) * len(fields))
>>> Node()
Node(val=None, left=None, right=None)
Run Code Online (Sandbox Code Playgroud)
设置Node.__new__.__defaults__为默认值.
>>> from collections import namedtuple
>>> Node = namedtuple('Node', 'val left right')
>>> Node.__new__.__defaults__ = (None,) * len(Node._fields)
>>> Node()
Node(val=None, left=None, right=None)
Run Code Online (Sandbox Code Playgroud)
设置Node.__new__.func_defaults为默认值.
>>> from collections import namedtuple
>>> Node = namedtuple('Node', 'val left right')
>>> Node.__new__.func_defaults = (None,) * len(Node._fields)
>>> Node()
Node(val=None, left=None, right=None)
Run Code Online (Sandbox Code Playgroud)
在所有版本的Python中,如果您设置的默认值少于namedtuple中存在的默认值,则默认值将应用于最右侧的参数.这允许您将一些参数保留为必需参数.
>>> Node.__new__.__defaults__ = (1,2)
>>> Node()
Traceback (most recent call last):
...
TypeError: __new__() missing 1 required positional argument: 'val'
>>> Node(3)
Node(val=3, left=1, right=2)
Run Code Online (Sandbox Code Playgroud)
这里有一个包装器,甚至可以让你(可选)将默认值设置为其他值None.这不支持必需的参数.
import collections
def namedtuple_with_defaults(typename, field_names, default_values=()):
T = collections.namedtuple(typename, field_names)
T.__new__.__defaults__ = (None,) * len(T._fields)
if isinstance(default_values, collections.Mapping):
prototype = T(**default_values)
else:
prototype = T(*default_values)
T.__new__.__defaults__ = tuple(prototype)
return T
Run Code Online (Sandbox Code Playgroud)
例:
>>> Node = namedtuple_with_defaults('Node', 'val left right')
>>> Node()
Node(val=None, left=None, right=None)
>>> Node = namedtuple_with_defaults('Node', 'val left right', [1, 2, 3])
>>> Node()
Node(val=1, left=2, right=3)
>>> Node = namedtuple_with_defaults('Node', 'val left right', {'right':7})
>>> Node()
Node(val=None, left=None, right=7)
>>> Node(4)
Node(val=4, left=None, right=7)
Run Code Online (Sandbox Code Playgroud)
Jus*_*Fay 139
我继承了namedtuple并覆盖了该__new__方法:
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)
Run Code Online (Sandbox Code Playgroud)
这保留了一种直观的类型层次结构,即伪造成类的工厂函数的创建不会.
Ign*_*ams 90
将它包装在一个函数中.
NodeT = namedtuple('Node', 'val left right')
def Node(val, left=None, right=None):
return NodeT(val, left, right)
Run Code Online (Sandbox Code Playgroud)
mon*_*ime 63
随着typing.NamedTuple在Python 3.6.1+,你可以同时提供一个默认值和类型标注为NamedTuple场.typing.Any如果您只需要前者,请使用:
from typing import Any, NamedTuple
class Node(NamedTuple):
val: Any
left: 'Node' = None
right: 'Node' = None
Run Code Online (Sandbox Code Playgroud)
用法:
>>> Node(1)
Node(val=1, left=None, right=None)
>>> n = Node(1)
>>> Node(2, left=n)
Node(val=2, left=Node(val=1, left=None, right=None), right=None)
Run Code Online (Sandbox Code Playgroud)
此外,如果您需要默认值和可选的可变性,Python 3.7将具有数据类(PEP 557),可以在某些(许多?)情况下替换namedtuples.
:参数和变量之后的表达式以及->函数之后的表达式)的一个怪癖是它们在定义时*进行评估.因此,由于"一旦类的整个主体被执行就会定义类名",'Node'上面类字段中的注释必须是字符串以避免NameError.
这种类型的提示被称为"前向引用"([1],[2]),而对于PEP 563, Python 3.7+将具有__future__导入(默认情况下在4.0中启用),允许使用前向引用没有引号,推迟评估.
* AFAICT仅在运行时不评估局部变量注释.(来源:PEP 526)
Tim*_*all 20
这是一个直接来自文档的示例:
可以使用_replace()来自定义原型实例来实现默认值:
Run Code Online (Sandbox Code Playgroud)>>> Account = namedtuple('Account', 'owner balance transaction_count') >>> default_account = Account('<owner name>', 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")
Run Code Online (Sandbox Code Playgroud)
但是,我更喜欢这里给出的其他一些答案.我只是想补充一下这个完整性.
jte*_*ace 19
我不确定是否有一个简单的方法只有内置的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)
Run Code Online (Sandbox Code Playgroud)
Gus*_*son 13
这是一个更紧凑的版本,灵感来自justinfay的答案:
from collections import namedtuple
from functools import partial
Node = namedtuple('Node', ('val left right'))
Node.__new__ = partial(Node.__new__, left=None, right=None)
Run Code Online (Sandbox Code Playgroud)
Ant*_*ile 11
在python3.7 +中有一个全新的defaults = keyword参数.
默认值可以是
None或可以迭代的默认值.由于具有默认值的字段必须位于没有默认值的任何字段之后,因此默认值将应用于最右侧的参数.例如,如果字段名是['x', 'y', 'z']和默认值(1, 2),x则将是必需参数,y默认为1,z默认为2.
用法示例:
$ ./python
Python 3.7.0b1+ (heads/3.7:4d65430, Feb 1 2018, 09:28:35)
[GCC 5.4.0 20160609] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> from collections import namedtuple
>>> nt = namedtuple('nt', ('a', 'b', 'c'), defaults=(1, 2))
>>> nt(0)
nt(a=0, b=1, c=2)
>>> nt(0, 3)
nt(a=0, b=3, c=2)
>>> nt(0, c=3)
nt(a=0, b=1, c=3)
Run Code Online (Sandbox Code Playgroud)
简短,简单,不会导致人们使用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))
Run Code Online (Sandbox Code Playgroud)
一个稍微扩展的示例,用于初始化所有缺少的参数None:
from collections import namedtuple
class Node(namedtuple('Node', ['value', 'left', 'right'])):
__slots__ = ()
def __new__(cls, *args, **kwargs):
# initialize missing kwargs with None
all_kwargs = {key: kwargs.get(key) for key in cls._fields}
return super(Node, cls).__new__(cls, *args, **all_kwargs)
Run Code Online (Sandbox Code Playgroud)
Python 3.7:defaults在namedtuple定义中引入了param。
文档中显示的示例:
>>> Account = namedtuple('Account', ['type', 'balance'], defaults=[0])
>>> Account._fields_defaults
{'balance': 0}
>>> Account('premium')
Account(type='premium', balance=0)
Run Code Online (Sandbox Code Playgroud)
在这里阅读更多。