26

很多时候,我发现自己在编写一些琐碎的数据类型,比如

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 大师可能想炫耀一些元类黑客或类工厂,以使我的生活更轻松?

4

6 回答 6

23
>>> 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='')
于 2012-12-18T12:52:22.533 回答
10

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中存在数据类,因此这是定义 DTO 的首选方式,也为了向后兼容,您可以使用attrs库。

于 2017-01-25T21:25:36.317 回答
6

python 中是否有任何流行的习语可以通过命名访问器派生快速...数据类型?

数据库。他们完成了这个确切的需求。

一些答案提到了数据类,但这里有一个例子。

代码

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

这是有关数据类详细信息的指南

于 2019-09-13T17:36:51.303 回答
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

这可能看起来像黑魔法——起初它对我有用,但它都记录在数据模型中并在这篇文章中进行了讨论。

在行动:

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

请注意,正如所写,将 a 传入tupleas可能是安全的defaultstuple但是,通过确保函数中有一个合理的对象,您可以轻松获得更多花哨的东西。

于 2012-12-18T13:32:34.380 回答
1

可以帮助您使样板代码更通用的另一种方法是对(本地)变量字典的迭代。这使您可以将变量放在列表中,并在循环中处理这些变量。例如:

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'

从有效利用资源的角度来看,这种方法当然不是最理想的。

于 2012-12-18T13:51:35.840 回答
1

如果使用 Python 3.7,您可以使用数据类;数据类可以被认为是“具有默认值的可变命名元组”

https://docs.python.org/3/library/dataclasses.html

https://www.python.org/dev/peps/pep-0557/

于 2019-05-08T21:23:40.293 回答