Python:快速而肮脏的数据类型 (DTO)
Posted
技术标签:
【中文标题】Python:快速而肮脏的数据类型 (DTO)【英文标题】:Python: Quick and dirty datatypes (DTO) 【发布时间】:2012-12-05 16:27:25 【问题描述】:很多时候,我发现自己在编写一些琐碎的数据类型,比如
class Pruefer:
def __init__(self, ident, maxNum=float('inf'), name=""):
self.ident = ident
self.maxNum = maxNum
self.name = name
虽然这非常有用(显然我不想用匿名 3 元组替换上面的内容),但它也是非常样板的。
现在,例如,当我想在字典中使用该类时,我必须添加更多样板,例如
def __hash__(self):
return hash(self.ident, self.maxNum, self.name)
我承认在我的所有样板类中识别一般模式可能很困难,但是我想提出这个问题:
有没有 python 中的流行习语用命名访问器派生快速和肮脏的数据类型?
或者,如果没有,也许 Python 大师可能想炫耀一些元类黑客或类工厂以使我的生活更轻松?
【问题讨论】:
我认为namedtuple
已经足够好了(添加了代码示例的完整答案)
namedtuple
现在允许 3.7+ 中的默认值
【参考方案1】:
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
详情
你得到:
漂亮的代表 默认值 散列 点属性访问 ...更多你没有(直接)得到:
元组解包(与 namedtuple 不同)这里是guide,关于数据类的详细信息。
【讨论】:
【参考方案2】:如果使用 Python 3.7,您可以使用数据类;数据类可以被认为是“具有默认值的可变命名元组”
https://docs.python.org/3/library/dataclasses.html
https://www.python.org/dev/peps/pep-0557/
【讨论】:
【参考方案3】: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')
它与命名元组相同,但保存注释并允许使用 mypy 等静态类型分析器检查类型。
更新:15.05.2018
现在,在 Python 3.7 中存在 dataclasses,因此这是定义 DTO 的首选方式,也为了向后兼容,您可以使用 attrs 库。
【讨论】:
【参考方案4】:可以帮助您使样板代码更通用的另一种方法是对(本地)变量字典的迭代。这使您可以将变量放在列表中,并在循环中处理这些变量。例如:
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'
从有效利用资源的角度来看,这种方法当然不是最理想的。
【讨论】:
【参考方案5】:对于 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
对象,您可以轻松获得更多花哨。
【讨论】:
【参考方案6】:>>> 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='')
【讨论】:
太好了!你是否也知道一些可以让我拥有默认值的东西? @JoSo -- 你可以有一个工厂函数,它有默认值并返回一个Pruefer
实例。
这可能只有我一个人,但我更喜欢("ident","maxNum","name")
,而不是带有空格分隔字符串的版本......这对我来说似乎更明显。
@mgilson:文档说你也可以使用["a1","a2","a3"]
。 (但我个人对较少墨水的“a1 a2 a3”很好。想想perl
的qw(a1 a2 a3)
;>)
@Duncan -- 我个人喜欢在事后添加默认值:Pruefer.__new__.func_defaults=(1,float('inf'),"")
(见我迟到的答案)以上是关于Python:快速而肮脏的数据类型 (DTO)的主要内容,如果未能解决你的问题,请参考以下文章
ruby CarrierWave带有来自雾的备用URL,快速而肮脏。对于将生产数据提取到开发中非常有用,以防万一你必须全力以赴
在配置了 s2Member 的 WordPress 中获取“失效”成员的快速而肮脏的方法是啥?
ruby 快速而肮脏的脚本,以PDF格式从AT&T获取最新账单。
python 快速和肮脏的查克诺里斯python脚本。用于我们的SVN post commit钩子。如果您没有请求库:yum install pytho
scss 对于zurb基金会问题#2789来说,这是一种快速而又肮脏的解决方案。它没有经过很好的测试,但我认为它可能会节省一些时间