python - namedtuple和可选关键字参数的默认值

我正在尝试将一个冗长的空洞“数据”类转换为一个命名元组。 我的班级目前看起来像这样:

class Node(object):

def __init__(self, val, left=None, right=None):

self.val = val

self.left = left

self.right = right

转换为Node(val)之后,它看起来像:

from collections import namedtuple

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

但这里有一个问题。 我的原始类允许我传入一个值,并使用命名/关键字参数的默认值来处理默认值。 就像是:

class BinaryTree(object):

def __init__(self, val):

self.root = Node(val)

但是这对我重构的名为元组的情况不起作用,因为它希望我传递所有字段。 我当然可以将Node(val)的出现更换为Node(val, None, None),但这不是我喜欢的。

那么有没有一个好的技巧可以让我的重写成功而不会增加很多代码复杂性(元编程)或者我应该吞下药丸并继续“搜索和替换”?:)

21个解决方案

354 votes

Python 3.7

使用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)

在Python 3.7之前

将None设置为默认值。

>>> 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)

在Python 2.6之前

将None设置为默认值。

>>> 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)

订购

在所有版本的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)

Python 2.6到3.6的包装器

这是你的包装器,甚至可以让你(可选)将默认值设置为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

例:

>>> 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)

Mark Lodato answered 2019-03-13T03:27:08Z

133 votes

我子类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)

这保留了一种直观的类型层次结构,即伪造成类的工厂函数的创建不会。

justinfay answered 2019-03-13T03:27:41Z

87 votes

将它包装在一个函数中。

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

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

return NodeT(val, left, right)

Ignacio Vazquez-Abrams answered 2019-03-13T03:28:08Z

44 votes

使用Python 3.6.1+中的__future__,您可以为NamedTuple字段提供默认值和类型注释。 如果您只需要前者,请使用__future__:

from typing import Any, NamedTuple

class Node(NamedTuple):

val: Any

left: 'Node' = None

right: 'Node' = None

用法:

>>> 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)

此外,如果您需要默认值和可选的可变性,Python 3.7将具有数据类(PEP 557),可以在某些(许多?)情况下替换namedtuples。旁注:Python中注释的当前规范的一个怪癖(参数和变量之后的表达式__future__之后以及函数之后的->之后的表达式)是它们在定义时*进行评估。 因此,由于“类名称在类的整个主体被执行后定义”,因此上面类字段中的'Node'的注释必须是字符串以避免NameError。

这种类型的提示被称为“前向引用”([1],[2]),而PEP 563 Python 3.7+将具有__future__导入(默认情况下在4.0中启用),允许使用前向 没有引号的引用,推迟了他们的评价。

* AFAICT仅在运行时不评估局部变量注释。 (来源:PEP 526)

monk-time answered 2019-03-13T03:29:07Z

19 votes

我不确定是否有一个简单的方法只有内置的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)

jterrace answered 2019-03-13T03:29:34Z

19 votes

这是一个直接来自文档的示例:

可以使用_replace()来自定义a来实现默认值   原型实例:

>>> Account = namedtuple('Account', 'owner balance transaction_count')

>>> default_account = 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")

但是,我更喜欢这里给出的其他一些答案。 我只是想补充一下这个完整性。

Tim Tisdall answered 2019-03-13T03:30:30Z

12 votes

这是一个更紧凑的版本,灵感来自justinfay的答案:

from collections import namedtuple

from functools import partial

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

Node.__new__ = partial(Node.__new__, left=None, right=None)

Gustav Larsson answered 2019-03-13T03:30:55Z

8 votes

在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)

Anthony Sottile answered 2019-03-13T03:31:34Z

5 votes

使用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)

Dennis Golomazov answered 2019-03-13T03:32:03Z

4 votes

你也可以用这个:

import inspect

def namedtuple_with_defaults(type, default_value=None, **kwargs):

args_list = inspect.getargspec(type.__new__).args[1:]

params = dict([(x, default_value) for x in args_list])

params.update(kwargs)

return type(**params)

这基本上使您可以构造具有默认值的任何命名元组,并仅覆盖您需要的参数,例如:

import collections

Point = collections.namedtuple("Point", ["x", "y"])

namedtuple_with_defaults(Point)

>>> Point(x=None, y=None)

namedtuple_with_defaults(Point, x=1)

>>> Point(x=1, y=None)

acerisara answered 2019-03-13T03:32:35Z

4 votes

结合@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'

teodor answered 2019-03-13T03:33:17Z

4 votes

简短,简单,并且不会导致人们不正确地使用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))

Elliot Cameron answered 2019-03-13T03:33:47Z

4 votes

Python 3.7:在namedtuple定义中引入defaults param。

示例如文档中所示:

>>> Account = namedtuple('Account', ['type', 'balance'], defaults=[0])

>>> Account._fields_defaults

{'balance': 0}

>>> Account('premium')

Account(type='premium', balance=0)

在这里。

Julian Camilleri answered 2019-03-13T03:34:27Z

3 votes

我发现这个版本更容易阅读:

from collections import namedtuple

def my_tuple(**kwargs):

defaults = {

'a': 2.0,

'b': True,

'c': "hello",

}

default_tuple = namedtuple('MY_TUPLE', ' '.join(defaults.keys()))(*defaults.values())

return default_tuple._replace(**kwargs)

这不是需要两次创建对象的效率,但是你可以通过在模块中定义默认的duple并让函数执行替换行来改变它。

Dave31415 answered 2019-03-13T03:35:01Z

2 votes

由于您使用namedtuple作为数据类,您应该知道python 3.7将为此目的引入namedtuple装饰器 - 当然它具有默认值。

来自文档的一个例子:

@dataclass

class C:

a: int # 'a' has no default value

b: int = 0 # assign a default value for 'b'

比黑客namedtuple更清洁,可读和可用。不难预测namedtuples的使用将随着3.7的采用而下降。

P-Gn answered 2019-03-13T03:35:38Z

1 votes

受这个对不同问题的回答的启发,这是我提出的基于元类的解决方案,并使用super(正确处理未来的子计算)。 这与justinfay的回答非常相似。

from collections import namedtuple

NodeTuple = namedtuple("NodeTuple", ("val", "left", "right"))

class NodeMeta(type):

def __call__(cls, val, left=None, right=None):

return super(NodeMeta, cls).__call__(val, left, right)

class Node(NodeTuple, metaclass=NodeMeta):

__slots__ = ()

然后:

>>> Node(1, Node(2, Node(4)),(Node(3, None, Node(5))))

Node(val=1, left=Node(val=2, left=Node(val=4, left=None, right=None), right=None), right=Node(val=3, left=None, right=Node(val=5, left=None, right=None)))

Alexey answered 2019-03-13T03:36:07Z

0 votes

使用我的metaclass库中的metaclass类,并使用exec语法,这非常简单:

from aenum import NamedTuple

class Node(NamedTuple):

val = 0

left = 1, 'previous Node', None

right = 2, 'next Node', None

一个潜在的缺点是对于具有默认值的任何属性需要metaclass字符串(对于简单属性,它是可选的)。 在使用中它看起来像:

>>> Node()

Traceback (most recent call last):

...

TypeError: values not provided for field(s): val

>>> Node(3)

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

这个优势超过了metaclass:

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)

是简单,以metaclass为基础而不是exec。

Ethan Furman answered 2019-03-13T03:36:56Z

0 votes

另一种方案:

import collections

def defaultargs(func, defaults):

def wrapper(*args, **kwargs):

for key, value in (x for x in defaults[len(args):] if len(x) == 2):

kwargs.setdefault(key, value)

return func(*args, **kwargs)

return wrapper

def namedtuple(name, fields):

NamedTuple = collections.namedtuple(name, [x[0] for x in fields])

NamedTuple.__new__ = defaultargs(NamedTuple.__new__, [(NamedTuple,)] + fields)

return NamedTuple

用法:

>>> Node = namedtuple('Node', [

... ('val',),

... ('left', None),

... ('right', None),

... ])

__main__.Node

>>> Node(1)

Node(val=1, left=None, right=None)

>>> Node(1, 2, right=3)

Node(val=1, left=2, right=3)

sirex answered 2019-03-13T03:37:21Z

0 votes

这是一个简短的通用答案,带有一个带有默认参数的命名元组的良好语法:

import collections

def dnamedtuple(typename, field_names, **defaults):

fields = sorted(field_names.split(), key=lambda x: x in defaults)

T = collections.namedtuple(typename, ' '.join(fields))

T.__new__.__defaults__ = tuple(defaults[field] for field in fields[-len(defaults):])

return T

用法:

Test = dnamedtuple('Test', 'one two three', two=2)

Test(1, 3) # Test(one=1, three=3, two=2)

精缩:

def dnamedtuple(tp, fs, **df):

fs = sorted(fs.split(), key=df.__contains__)

T = collections.namedtuple(tp, ' '.join(fs))

T.__new__.__defaults__ = tuple(df[i] for i in fs[-len(df):])

return T

Matthew D. Scholefield answered 2019-03-13T03:37:57Z

0 votes

jterrace使用recordtype的答案很棒,但是该库的作者建议使用他的namedlist项目,它提供了mutable(namedlist)和immutable(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)

nbarraille answered 2019-03-13T03:38:27Z

-1 votes

这是Mark Lodato包装器的一个不太灵活但更简洁的版本:它将字段和默认值作为字典。

import collections

def namedtuple_with_defaults(typename, fields_dict):

T = collections.namedtuple(typename, ' '.join(fields_dict.keys()))

T.__new__.__defaults__ = tuple(fields_dict.values())

return T

例:

In[1]: fields = {'val': 1, 'left': 2, 'right':3}

In[2]: Node = namedtuple_with_defaults('Node', fields)

In[3]: Node()

Out[3]: Node(val=1, left=2, right=3)

In[4]: Node(4,5,6)

Out[4]: Node(val=4, left=5, right=6)

In[5]: Node(val=10)

Out[5]: Node(val=10, left=2, right=3)

Li-Wen Yip answered 2019-03-13T03:38:57Z

pythonnamedtuple定义类型_python - namedtuple和可选关键字参数的默认值相关推荐

  1. pythonnamedtuple定义类型_python namedtuple的使用

    首先,我会介绍下使用namedtuple所需要了解的基本概念,然后讲解如何使用namedtuple,最后使用namedtuple来创建一摞纸牌.理解这些之后,就可以权衡利弊,并在生产中使用 基本概念 ...

  2. 关于学习Python的一点学习总结(27->关键字参数和默认值)

    64.关键字参数和默认值 先举个例子: def Hello_1(name,gets):print("{},{}".format(name,gets))>>> na ...

  3. python函数调用位置_python函数定义,调用,传参,位置参数及关键字参数,返回值

    使用函数是真正开始编程的第一步,函数y=f(x)我们并不陌生,对x进行一顿操作得到一个值y.给不同的x,进行相同的操作,得到相应的y值. 程序层面函数是执行特定任务的一段代码,将一段代码定义成函数并为 ...

  4. python函数用法详解2(变量的作用域(全局变量、局部变量)、共享全局变量、函数返回值、函数的参数(位置参数、关键字参数、默认参数、不定长参数)、拆包、交换变量值、引用、可变和不可变类型)

    1. 变量作⽤域         变量作⽤域指的是变量⽣效的范围,主要分为两类:局部变量和全局变量. 局部变量         定义在函数体内部的变量,即只在函数体内部⽣效. def testA(): ...

  5. python函数支持哪些参数类型_Python函数的几种参数类型

    以下代码均以Python3为基础理解. 初识Python函数 大部分常见的语言如C.Java.PHP.C#.JavaScript等属于C系语言,Python不属于他们中的一员(ruby亦然).在这些语 ...

  6. python关键字参数必须位于位置参数之前_python函数中的参数(关键字参数,默认参数,位置参数,不定长参数)...

    默认参数:定义函数的时候给定变量一个默认值. def num(age=1): 位置参数:调用函数的时候根据定义函数时的形参位置和实参位置进行引用. 关键字参数:如果定义的函数中含有关键字参数,调用函数 ...

  7. python函数参数学习_python学习笔记-11.函数参数和返回值进阶

    1. 函数参数和返回值的作用 函数根据有没有参数以及有没有返回值,可以相互组合,共有4种形式: 无参数,无返回值 无参数,有返回值 有参数,无返回值 有参数,有返回值 定义函数时,是否接收参数,或者是 ...

  8. 【笔记】python的传递实参:位置实参、关键字实参、默认值、等效的函数调用、避免实参错误

    文章目录 一.传递实参 1.位置实参 2.关键字实参 3.默认值 4.等效的函数调用 5.避免实参错误 一.传递实参 函数定义中可能包含多个形参,因此函数调用中也可能包含多个实参.向函数传递实参的方式 ...

  9. python非可选参数_带plac的可选参数的默认值和非默认值 - python

    我的目标是使用plac在命令行中运行脚本,所有参数均为可选,并具有默认值. 不带参数:python my_script.py.它运行完美. 带有参数:python my_script.py -r &q ...

最新文章

  1. 视野逐渐变暗之跳转场景
  2. anaconda python 版本对应关系
  3. P1629邮递员送信与P1342请柬与P1821银牛派队研制联合胜利
  4. SQL Server DATEDIFF() 函数计算时间差
  5. oracle导入substring,java中substring()和oracle中substr()区别
  6. liunx 命令手册 (chm)
  7. 20200410:路径总和 Ⅰ Ⅱ(leetcode112 /113)
  8. SQL Server 2012 无人值守安装(加入新实例)
  9. POJ-2078 Matrix,暴力枚举!
  10. Linux程序文件状态,linux 文件状态标识和IO操作方式
  11. Android开发之实现多次点击事件
  12. 推荐系统(7):推荐算法之基于协同过滤推荐算法
  13. 做自媒体数据分析的一些工具
  14. 红米Pro 2016020 2016021通刷官方线刷包_救砖包_解账户锁
  15. springboot微服务使用Feign远程调用失败
  16. matlab设计高通系统,用matlab设计高通滤波器雪比切夫、fir两种方法 课程设计HPF.doc...
  17. 记一次app爬虫sign破解
  18. PS系列之 -- 通道抠图进阶 : 用通道抠取头发
  19. iOS抓包工具Charles 4.0.1破解版
  20. CSDN RSS客户端订阅说明

热门文章

  1. 阿里iconfont图库官网网址
  2. 《软件需求最佳实践》阅读笔记02
  3. UILabel 设置行间距
  4. IBM AppScan 安全漏洞问题修复(.net)
  5. vsftp 一键安装包
  6. JavaScript把客户端时间转换为北京时间
  7. 如何用HttpWebRequest下载gzip, deflate压缩的页面
  8. String引起的OutOfMemory异常 + 如何计算C#对象所占内存的大小
  9. PhpMyAdmin 配置文件现在需要一个短语密码的解决方法
  10. java抛出异常thorw和throws的用法