如何在Python中创建只读类属性?

80 投票
5 回答
46024 浏览
提问于 2025-04-16 00:59

基本上,我想做的事情是这样的:

class foo:
    x = 4
    @property
    @classmethod
    def number(cls):
        return x

然后我希望下面的代码能够正常运行:

>>> foo.number
4

可惜的是,上面的代码并没有成功。它给我的不是 4,而是 <property object at 0x101786c58>。有没有办法能实现我想要的效果呢?

5 个回答

17

我同意unubtu的回答;这个方法看起来有效,不过在Python 3(具体来说,我在使用Python 3.4时遇到了问题)上,这种特定的写法是行不通的。下面是要在Python 3.4中正确写出这个模式的方法:

class MetaFoo(type):
   @property
   def number(cls):
      return cls.x

class Foo(metaclass=MetaFoo):
   x = 4

print(Foo.number)
# 4

Foo.number = 6
# AttributeError: can't set attribute
76

这段代码会让 Foo.number 成为一个 只读 属性:

class MetaFoo(type):
    @property
    def number(cls):
        return cls.x

class Foo(object, metaclass=MetaFoo):
    x = 4

print(Foo.number)
# 4

Foo.number = 6
# AttributeError: can't set attribute

解释:通常使用 @property 的场景是这样的:

class Foo(object):
    @property
    def number(self):
        ...
foo = Foo()

Foo 中定义的属性对于它的实例来说是只读的。也就是说,如果你尝试执行 foo.number = 6,就会出现一个 AttributeError 错误。

类似地,如果你想让 Foo.number 也抛出 AttributeError,你需要在 type(Foo) 中设置一个属性。因此,这就需要用到元类。


需要注意的是,这种只读的属性并不是完全安全的,黑客可以通过修改 Foo 的类来让这个属性变得可写:

class Base(type): pass
Foo.__class__ = Base

# makes Foo.number a normal class attribute
Foo.number = 6   
print(Foo.number)

会打印出

6

或者,如果你想让 Foo.number 成为一个可设置的属性,

class WritableMetaFoo(type): 
    @property
    def number(cls):
        return cls.x
    @number.setter
    def number(cls, value):
        cls.x = value
Foo.__class__ = WritableMetaFoo

# Now the assignment modifies `Foo.x`
Foo.number = 6   
print(Foo.number)

也会打印出

6
54

当你从一个类中访问 property 描述符时,它总是会返回自己(也就是说,当 instance 在它的 __get__ 方法中是 None 时)。

如果你想要的不是这样,你可以写一个新的描述符,它总是使用类对象(owner),而不是实例:

>>> class classproperty(object):
...     def __init__(self, getter):
...         self.getter= getter
...     def __get__(self, instance, owner):
...         return self.getter(owner)
... 
>>> class Foo(object):
...     x= 4
...     @classproperty
...     def number(cls):
...         return cls.x
... 
>>> Foo().number
4
>>> Foo.number
4

撰写回答