Python中的类级别只读属性

时间:2009-11-14 20:06:06

标签: python

有没有办法在Python中创建一个类级别的只读属性?例如,如果我有一个班级Foo,我想说:

x = Foo.CLASS_PROPERTY

但阻止任何人说:

Foo.CLASS_PROPERTY = y

修改 我喜欢Alex Martelli's solution的简单性,但不喜欢它需要的语法。他和~unutbu's answer的灵感都启发了以下解决方案,即更接近符合我所寻求的精神:

class const_value (object):
    def __init__(self, value):
        self.__value = value

    def make_property(self):
        return property(lambda cls: self.__value)

class ROType(type):
    def __new__(mcl,classname,bases,classdict):
        class UniqeROType (mcl):
            pass

        for attr, value in classdict.items():
            if isinstance(value, const_value):
                setattr(UniqeROType, attr, value.make_property())
                classdict[attr] = value.make_property()

        return type.__new__(UniqeROType,classname,bases,classdict)

class Foo(object):
    __metaclass__=ROType
    BAR = const_value(1)
    BAZ = 2

class Bit(object):
    __metaclass__=ROType
    BOO = const_value(3)
    BAN = 4

现在,我明白了:

Foo.BAR
# 1
Foo.BAZ
# 2
Foo.BAR=2
# Traceback (most recent call last):
#   File "<stdin>", line 1, in <module>
# AttributeError: can't set attribute
Foo.BAZ=3
#

我更喜欢这个解决方案,因为:

  • type(X).foo = ...
  • 一样,成员获得内联而非事后声明
  • 成员的值在实际类的代码中设置,而不是在元类的代码中。

它仍然不理想,因为:

  • 我必须设置__metaclass__才能正确解释const_value个对象。
  • const_value不像普通值那样“表现”。例如,我无法将其用作类中方法的参数的默认值。

3 个答案:

答案 0 :(得分:9)

现有的解决方案有点复杂 - 只需确保某个组中的每个类都有一个唯一的元类,然后在自定义元类上设置一个普通的只读属性。即:

>>> class Meta(type):
...   def __new__(mcl, *a, **k):
...     uniquemcl = type('Uniq', (mcl,), {})
...     return type.__new__(uniquemcl, *a, **k)
... 
>>> class X: __metaclass__ = Meta
... 
>>> class Y: __metaclass__ = Meta
... 
>>> type(X).foo = property(lambda *_: 23)
>>> type(Y).foo = property(lambda *_: 45)
>>> X.foo
23
>>> Y.foo
45
>>> 

这实际上要简单得多,因为它只是基于这样一个事实:当你获得一个实例的属性描述符时,会在类上查找(所以当你在类的类中查看类的属性描述符时),并且使class / metaclass独特并不是特别困难。

哦,当然还有:

>>> X.foo = 67
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: can't set attribute

只是为了确认它确实是只读的!

答案 1 :(得分:4)

Pynt引用的ActiveState solution使ROClass的实例具有只读属性。您的问题似乎在询问类本身是否具有只读属性。

以下是一种基于Raymond Hettinger's comment的方法:

#!/usr/bin/env python
def readonly(value):
    return property(lambda self: value)

class ROType(type):
    CLASS_PROPERTY = readonly(1)

class Foo(object):
    __metaclass__=ROType

print(Foo.CLASS_PROPERTY)
# 1

Foo.CLASS_PROPERTY=2
# AttributeError: can't set attribute

这个想法是这样的:首先考虑Raymond Hettinger的解决方案:

class Bar(object):
    CLASS_PROPERTY = property(lambda self: 1)
bar=Bar()
bar.CLASS_PROPERTY=2

它显示了一种为bar提供只读属性的相对简单的方法。

请注意,您必须添加CLASS_PROPERTY = property(lambda self: 1) 行到bar类的定义,而不是bar自身。

因此,如果您希望类Foo具有只读属性,则Foo的父类必须定义CLASS_PROPERTY = property(lambda self: 1)

类的父类是元类。因此我们将ROType定义为元类:

class ROType(type):
    CLASS_PROPERTY = readonly(1)

然后我们让Foo的父类为ROType:

class Foo(object):
    __metaclass__=ROType

答案 2 :(得分:0)

ActiveState上找到了这个:

# simple read only attributes with meta-class programming

# method factory for an attribute get method
def getmethod(attrname):
    def _getmethod(self):
        return self.__readonly__[attrname]

    return _getmethod

class metaClass(type):
    def __new__(cls,classname,bases,classdict):
        readonly = classdict.get('__readonly__',{})
        for name,default in readonly.items():
            classdict[name] = property(getmethod(name))

        return type.__new__(cls,classname,bases,classdict)

class ROClass(object):
    __metaclass__ = metaClass
    __readonly__ = {'a':1,'b':'text'}


if __name__ == '__main__':
    def test1():
        t = ROClass()
        print t.a
        print t.b

    def test2():
        t = ROClass()
        t.a = 2

    test1()

请注意,如果您尝试设置只读属性(t.a = 2),python将引发AttributeError