防止/修改访问类变量

10
有没有一种方法可以像覆盖实例变量的__setattr__一样防止或更改Python中的类变量访问?
请注意,此问题的标题错误,实际上是指实例变量,而不是类变量。
基于阅读关于(明显)死亡陷阱__slots__的多篇帖子,我更喜欢不走这条路(而且我还没有足够的了解来知道它是否做到了我所要求的)。
示例:
class A(object):

    foo = "don't change me"

    def __setattr__(self, name, value):
        raise ValueError

if __name__ == '__main__':
a1 = A()
print a1.foo # don't change me
print A.foo  # don't change me

A.foo = 'bar' # ideally throw an exception or something here
print A.foo   # bar
a2 = A()
print a1.foo  # bar
print a2.foo  # bar

1
Mike链接的问题有一个评论,其中链接到http://docs.python.org/library/functions.html#property,这可能是你正在寻找的内容。 - Brenden Brown
@mike 是的,看起来是重复的。糟糕。这个问题可能应该被关闭为重复问题。 - elhefe
2个回答

6

没错!你需要以完全相同的方式执行。

__setattr__方法控制对该类实例设置属性。在Python中,所有内容都是对象。而且对象有类别。因此,您只需要安排您的类成为某个具有__setattr__方法的类的实例,就像您想要防止实例修改其属性一样!

一个类的类别称为元类。默认元类是type:类型的“类型”,或者是“类的类”。您需要制作type的子类,以不允许其实例设置属性(正如您制作object的子类,以不允许其实例在执行这种操作时设置属性)。

class CantTouchThis(type):
    def __setattr__(cls, attr, value):
        raise Exception("NO!")

class SomeClass(object):
    __metaclass__ = CantTouchThis

请注意SomeClass基类(base class(es))SomeClass类(class)之间的区别。 SomeClassobject的子类(subclass)。它是CantTouchThis的一个实例(instance)。
几乎所有在实例级别(instance level)上工作的东西都可以通过元类(metaclasses)同样应用于类级别(class level); 类只是像其他一切一样的实例。它们之所以具有独特的优雅行为,是因为type的方法和实现(完全如...等等)。

不适用于Python 3.8。 - NiranjanBhat
@NiranjanBhat 在 SomeClass 中,可以在类签名中使用 metaclass=CantTouchThis 替换 __metaclass__ 以达到相同的效果。 - srm

3

您离成功已经不远了,只需要将__setattr__方法移到元类中:

class ReadOnlyClass(type):
    def __setattr__(self, name, value):
        raise ValueError(name)
    
class A(object, metaclass=ReadOnlyClass):
    foo = "don't change me"
    
>>> a1 = A()
>>> a1.foo
"don't change me"
>>> A.foo
"don't change me"
>>> A.foo = 'bar'

Traceback (most recent call last):
  File "<pyshell#13>", line 1, in <module>
    A.foo = 'bar'
  File "<pyshell#4>", line 3, in __setattr__
    raise ValueError, name
ValueError: foo
>>> print A.foo
don't change me
>>> a2 = A()
>>> a2.foo
"don't change me"
>>> a2.foo = 'x'
>>> a2.foo
'x'

请注意,虽然元类__setattr__可以防止您更改类中的属性,但它无法阻止您使用实例属性隐藏它们:如果这是您想要的效果,则需要在两个位置定义__setattr__
注:已更新为Python 3.x语法,而非古老的Python 2.x。

在Python3.8中无法工作。 - NiranjanBhat
1
那是因为我在2012年编写的时候,Python 3还很年轻,几乎没有被使用,而Python 2仍然很流行。现在我已经编辑过它,使其使用Python 3语法了。 - Duncan

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