很多时候,我发现自己编写了像
这样的简单数据类型def Pruefer:
def __init__(self, ident, maxNum=float('inf'), name=""):
self.ident = ident
self.maxNum = maxNum
self.name = name
虽然这非常有用(显然我不想用匿名的3元组替换上面的内容),但它也是非常有用的。
现在例如,当我想在dict中使用该类时,我必须添加更多样板
def __hash__(self):
return hash(self.ident, self.maxNum, self.name)
我承认在我的所有样板类中都可能很难识别出一般模式,但我仍然喜欢这个问题:
有没有 python中流行的习惯用来获取带有命名访问器的快速和脏数据类型?
或许如果没有,也许Python大师可能想展示一些元类黑客或类工厂来让我的生活更轻松?
答案 0 :(得分:20)
>>> from collections import namedtuple
>>> Pruefer = namedtuple("Pruefer", "ident maxNum name")
>>> pr = Pruefer(1,2,3)
>>> pr.ident
1
>>> pr.maxNum
2
>>> pr.name
3
>>> hash(pr)
2528502973977326415
要提供默认值,您需要做更多...简单的解决方案是使用__new__
方法的重新定义编写子类:
>>> class Pruefer(namedtuple("Pruefer", "ident maxNum name")):
... def __new__(cls, ident, maxNum=float('inf'), name=""):
... return super(Pruefer, cls).__new__(cls, ident, maxNum, name)
...
>>> Pruefer(1)
Pruefer(ident=1, maxNum=inf, name='')
答案 1 :(得分:2)
Python 3.6中最有前途的事情之一是变量注释。它们允许以下一种方式将namedtuple定义为类:
In [1]: from typing import NamedTuple
In [2]: class Pruefer(NamedTuple):
...: ident: int
...: max_num: int
...: name: str
...:
In [3]: Pruefer(1,4,"name")
Out[3]: Pruefer(ident=1, max_num=4, name='name')
与namedtuple相同,但保存注释并允许使用某些静态类型分析器(如mypy)检查类型。
更新:15.05.2018
现在,在Python 3.7中存在dataclasses,因此这是定义DTO的首选方式,也可以使用attrs库来实现向后兼容性。
答案 2 :(得分:1)
我没有太多可以添加Alexey Kachayev已经很好的答案 - 但是,可能有用的一件事是以下模式:
Pruefer.__new__.func_defaults = (1,float('inf'),"")
这将允许您创建一个工厂函数,该函数返回一个可以具有默认参数的新命名元组:
def default_named_tuple(name,args,defaults=None):
named_tuple = collections.namedtuple(name,args)
if defaults is not None:
named_tuple.__new__.func_defaults = defaults
return named_tuple
这可能看起来像黑魔法 - 它起初对我有用,但它全部记录在Data Model中并在this post中进行了讨论。
行动中:
>>> default_named_tuple("Pruefer", "ident maxNum name",(1,float('inf'),''))
<class '__main__.Pruefer'>
>>> Pruefer = default_named_tuple("Pruefer", "ident maxNum name",(1,float('inf'),''))
>>> Pruefer()
Pruefer(ident=1, maxNum=inf, name='')
>>> Pruefer(3)
Pruefer(ident=3, maxNum=inf, name='')
>>> Pruefer(3,10050)
Pruefer(ident=3, maxNum=10050, name='')
>>> Pruefer(3,10050,"cowhide")
Pruefer(ident=3, maxNum=10050, name='cowhide')
>>> Pruefer(maxNum=12)
Pruefer(ident=1, maxNum=12, name='')
仅将某些参数指定为默认值:
>>> Pruefer = default_named_tuple("Pruefer", "ident maxNum name",(float('inf'),''))
>>> Pruefer(maxNum=12)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: __new__() takes at least 2 arguments (2 given)
>>> Pruefer(1,maxNum=12)
Pruefer(ident=1, maxNum=12, name='')
请注意,正如所写的那样,将tuple
作为defaults
传递可能是安全的。但是,通过确保函数中有一个合理的tuple
对象,您可以轻松获得更多的幻想。
答案 3 :(得分:1)
另一种可能有助于您使锅炉板代码更通用的方法是对(本地)变量dicts的迭代。这使您可以将变量放在列表中并在循环中处理这些变量。例如:
class Pruefer:
def __init__(self, ident, maxNum=float('inf'), name=""):
for n in "ident maxNum name".split():
v = locals()[n] # extract value from local variables
setattr(self, n, v) # set member variable
def printMemberVars(self):
print("Member variables are:")
for k,v in vars(self).items():
print(" {}: '{}'".format(k, v))
P = Pruefer("Id", 100, "John")
P.printMemberVars()
给出:
Member Variables are:
ident: 'Id'
maxNum: '100'
name: 'John'
从有效利用资源的角度来看,这种方法当然不是最理想的。
答案 4 :(得分:0)
如果使用Python 3.7,则可以使用数据类;数据类可以被认为是“带有默认值的可变命名元组”
答案 5 :(得分:0)
Python中是否有任何流行的惯用语来派生具有命名访问器的快速...数据类型?
Dataclases。他们完全满足了这一需求。
一些答案提到了数据类,但这是一个示例。
代码
import dataclasses as dc
@dc.dataclass(unsafe_hash=True)
class Pruefer:
ident : int
maxnum : float = float("inf")
name : str = ""
演示
pr = Pruefer(1, 2.0, "3")
pr
# Pruefer(ident=1, maxnum=2.0, name='3')
pr.ident
# 1
pr.maxnum
# 2.0
pr.name
# '3'
hash(pr)
# -5655986875063568239
详细信息
您得到:
您没有(直接)得到:
这是有关数据类详细信息的guide。