如何在Python中动态创建属性?

23

假设我有这样一个类:

class Alphabet(object):
     __init__(self):
         self.__dict = {'a': 1, 'b': 2, ... 'z': 26}

     @property
     def a(self):
         return self.__dict['a']

     @property
     def b(self):
         return self.__dict['b']

     ...

     @property
     def z(self)
         return self.__dict['z']

定义这些属性将是一项漫长而繁琐的任务,而且似乎高度冗余。有没有一种方法可以动态创建这些属性?我知道可以使用内置的setattr动态创建属性,但我想要对读取/写入/删除访问进行控制(因此我想使用属性)。谢谢!


2
执行@property\ndef something()等同于something = property(something)。在这种情况下,这相当于执行self.setattr('a', property(lambda self: self.__dict['a'])) - Joel Cornett
2
@JoelCornett:你评论中的第二个语句是不正确的,你需要将其设置在类上而不是实例上:https://paste.aeum.net/show/106/。这样做的原因是因为[描述符的工作方式](http://docs.python.org/reference/datamodel.html#implementing-descriptors):*以下方法仅适用于包含该方法(所谓的描述符类)的类的实例出现在所有者类中(描述符必须在所有者的类字典或其父类之一的类字典中)。* - ThiefMaster
@ThiefMaster:我改正了 :) - Joel Cornett
2个回答

43

不要使用属性,而是实现以下方法:

  • __getattr__(self, name)
  • __setattr__(self, name, value)
  • __delattr__(self, name)

请参见http://docs.python.org/reference/datamodel.html#customizing-attribute-access

你的__getattr__方法可能如下所示:

def __getattr__(self, name):
    try:
        return self.__dict[name]
    except KeyError:
        msg = "'{0}' object has no attribute '{1}'"
        raise AttributeError(msg.format(type(self).__name__, name))

谢谢!非常简单易懂。 - Adrian Torrie
2
我猜这会失去 IDE 提供自动完成属性的功能。 - Michael Jaison G

-2

不要这样做。只需让类的使用者直接访问__dict,并相信他们不会搞砸它。记住,我们都是成年人!

Ned Batchelder解释得比我好:


避免在变量名中使用数据

这个问题让我想起了 Stack Overflow 或 #python IRC 频道上看到的其他问题:

  • 如何判断变量是否存在?
  • 如何将一个变量用作另一个变量的名称?
  • 如何将一个变量用作 SQL 表名的一部分?

所有这些问题都有一个共同点,那就是试图弥合两个领域之间的差距:程序中的数据和程序中数据的名称。每当发生这种情况时,这清楚地表明您需要在数据建模中提高一个级别。您需要一个字典而不是 26 个列表。您应该只有一个表,其中还有一列。


3
Ned有一个好观点,但这里的目标是封装。这不仅重要是为了防止错误(成年人也可���犯错),而且还是一种方便之举。类的内部代码很可能有充分的理由将字典视为字典,而让客户端代码将其视为属性集合。 - Karl Knechtel
4
直接访问__dict__的问题在于它很丑陋,特别是涉及到名称重整。有时,为客户端提供一些语法糖会起到不小的作用,更不用说通过提供间接层次来方便未来可能的更改了。 - martineau
关于@martineau的评论,名称混淆会使这个解决方案非常丑陋,这是因为dict前缀带有双下划线。而且在Python中,任何以下划线为前缀的东西都不应该被触及,这是一个未经宣传的规则。此外,如果您使用属性来定义类属性,则不希望对其进行修改。 - error 1044

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接