【问题标题】:creating Python classes with arbitrarily substituted attribute name使用任意替换的属性名称创建 Python 类
【发布时间】:2011-06-25 09:20:04
【问题描述】:

我很抱歉没有给这个问题一个更好的标题;我发布它的原因是我什至没有正确的术语来知道我在寻找什么。

我已经定义了一个具有“垃圾邮件”属性的类:

def SpamClass(对象): def __init__(self, arg): self.spam = arg def __str__(self): 返回 self.spam

我想创建一个(子/兄弟?)类,它具有完全相同的功能,但使用名为“eggs”而不是“spam”的属性:

def EggsClass(对象): def __init__(self, arg): self.eggs = arg def __str__(self): 返回self.eggs

概括地说,我如何创建具有任意属性名称的功能相同的类?当类有复杂的行为时,重复代码似乎很愚蠢。

更新:我同意这听起来像是糟糕的设计。澄清一下,我并不是想以这种愚蠢的方式解决特定问题。我只想知道如何在保留功能的同时任意命名对象__dict__ 的(非魔法)内容。考虑类似keys() 类对象的keys() 方法。人们使用符合约定的keys() 方法创建各种类,命名约定是一件好事。但是这个名字是随意的。如何在不手动替换源代码中的 /keys/spam/ 的情况下使用完全替换 keys()spam() 方法创建一个类?

重载__getattr__ 和朋友引用通用属性对我来说似乎不优雅和脆弱。如果子类重新实现这些方法,它必须适应这种行为。我宁愿让用户觉得只有一个带有命名属性的基类,可以天真地访问。

实际上,我可以想到一个合理的用例。假设您想要一个具有特殊属性的 mixin 类和一些密切相关的方法来操作或依赖于该属性。用户可能希望在重用底层行为的同时,为不同的类以不同的方式命名这个特殊属性(以匹配实际问题域中的名称或避免名称冲突)。

【问题讨论】:

  • 如果类相同,为什么需要不同的属性名?
  • 这真是一个非常糟糕的设计。

标签: python attributes introspection metaclass


【解决方案1】:

这是一种获得我认为你想要的效果的方法。

使用通用属性名称定义一个通用类。然后在每个子类中遵循http://docs.python.org/reference/datamodel.html#customizing-attribute-access 中的建议,使属性在外部看起来就像你想调用的那样调用它。

你对你所做的事情的描述让我觉得它有一种“代码味道”,我建议非常仔细地审查你的设计,看看这是否真的是你想要做的。但是你可以按照我的建议让它工作。

【讨论】:

    【解决方案2】:

    您还可以创建一个包含所有常见内容的超类,然后创建具有特定属性的子类。

    甚至:

    def SuperClass(object):
        specific_attribute = 'unset'
    
        def __init__(self, arg):
            setattr(self, specific_attribute, arg)
    
        def __str__(self):
            return getattr(self, specific_attribute)
    
    
    def EggClass(SuperClass):
        specific_attribute = 'eggs'
    

    【讨论】:

      【解决方案3】:

      您是否考虑过不要让事情过于复杂而只创建一个类? (因为无论如何它们都是相同的)

      class FoodClass(object):
          def __init__(self, foodname, arg):
              self.attrs = {foodname: arg}
              self.foodname = foodname
      
          def __str__(self):
              return self.attrs[foodname]
      

      如果你想要一些不错的构造函数,只需单独创建它们:

      def make_eggs(arg):
          return FoodClass('eggs', arg)
      
      def make_spam(arg):
          return FoodClass('spam', arg)
      

      【讨论】:

      • 抱歉,我认为这行不通。只有__init__可以直接访问arg,但其他方法也需要访问self.arg。此外,用这个额外的attrs 字典污染self.__dict__ 似乎很笨拙。
      【解决方案4】:

      要在运行时创建属性,只需将它们添加到类代码中的self.__dict__['foo'] = 'I'm foo' 中即可。

      【讨论】:

      • 如果一个类使用槽,或者有一些与self.__dict__不兼容的其他特性,这将不起作用。 setattr(self, 'name', ...) 确实是等价的,但它仍然是个坏主意...
      • 我只用过一次上面的,在一个基于继承创建属性的元类中。顺便说一句,我不知道一个类可以与setattr 不兼容。
      • 它不能,但它可能与self.__dict__不兼容(不兼容,我的意思是“做意想不到的事情”)。
      猜你喜欢
      • 1970-01-01
      • 2020-04-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-04-22
      • 1970-01-01
      相关资源
      最近更新 更多