【问题标题】:Python class without attribute boilerplate没有属性样板的 Python 类
【发布时间】:2013-06-14 17:31:38
【问题描述】:

我的许多类看起来像下面的类来表示帐户

class Account(object):
    def __init__(self, first, last, age, id, balance):
        self.first = first
        self.last = last
        self.age = age
        self.id = id
        self.balance = balance

    def _info(self):
        return self.first, self.last, self.age, self.id, self.balance

    def __eq__(self, other):
        return self._info == other._info()

    def __hash__(self):
        return hash((type(self), self.info()))

    def ... # other methods follow

但真正唯一相关的信息是我关心的属性列表first, last, age, id, balance。是否有标准方法来定义遵循此结构的 Python 类?

乍一看,我想到了namedtuple,但我不确定这是否允许我在事后添加其他方法。真的,我想要类似下面的东西

class Account(object):
    attributes = "first last age id balance"

    def ... # other methods

获得这个的最好方法是什么?

【问题讨论】:

    标签: python oop metaprogramming


    【解决方案1】:

    不确定它是多么地道,但以下满足您的要求:

    class Slottable:
        def __init__(self, *args):
            for slot, arg in zip(self.slots.split(' '), args):
                setattr(self, slot, arg)
    
        def _info(self):
            return tuple(getattr(self, attr) for attr in self.slots.split())
    
        def __eq__(self, other):
            return self._info() == other._info()
    
        def __hash__(self):
            return hash((type(self), self._info()))
    
    
    class Account(Slottable):
        slots = "first last age id balance"
    
        def fullname(self):
            return self.first + " " + self.last
    
    matt = Account("Matthew", "Smith", 28, 666, 1E6)
    john = Account("John", "Jones", 46, 667, 1E7)
    
    d = {matt: 5, john: 6}  # Hashable
    
    print matt.fullname()
    #=> "Matthew Smith"
    print john.fullname()
    #=> "John Jones"
    print matt == matt, matt == john
    #=> True False
    matt.age = 29  # Happy birthday!
    print matt.age
    #=> 29
    

    【讨论】:

    • Hrm,这不能很好地处理突变。特别是它不遵循单点真理原则。冗余数据保存在 __info 和每个属性中。
    • @MRocklin 突变?你为什么要那个?!? :-) 我编辑了示例以处理突变并将信息仅保留在属性字典中。
    【解决方案2】:

    Here are some recipes you can try:覆盖__setattr____dict____slots__ 和/或init。让我们知道什么对您有用。

    【讨论】:

      【解决方案3】:

      存在许多库来满足这一需求:attrsdataclassespydantic,......以及我在这个领域的新成员,pyfields

      选择主要取决于您需要或不需要的功能。 pyfields 专注于字段定义和可选的验证和转换,对您的类没有任何 约束。 可以原生的字段变得和python原生属性一样快,而需要回调(验证器/转换器)的字段是使用描述符实现的。

      您可以将自己的构造函数与

      from pyfields import field, init_fields
      
      class Account(object):
          first = field(doc="first name")
          last = field(doc="last name")
          age = field(doc="the age in years")
          id = field(doc="an identifier")
          balance = field(doc="current balance in euros")
      
          @init_fields
          def __init__(self, msg):
              print(msg)
      
      a = Account("hello, world!", first="s", last="marie", age=135, id=0, balance=-200000)
      print(vars(a))
      

      产量

      hello, world!
      {'balance': -200000, 'id': 0, 'age': 135, 'last': 'marie', 'first': 's'}
      

      与其他更多“一体式”库相比,pyfields 仅专注于字段和构造函数,具有“最小可行产品”的精神。因此,如果您还想要 dict 表示和转换、哈希、相等和比较,您应该使用另一个库将它们添加到您的类之上。我目前正在开发一个 mixture 库,为此提供混合类,具有与 a la carte 功能相同的理念 - 您可以在有或没有 pyfields 的情况下使用。 p>

      详情请见pyfields documentation。不要犹豫,提供反馈!

      【讨论】:

        猜你喜欢
        • 2016-08-28
        • 1970-01-01
        • 1970-01-01
        • 2014-07-14
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多