繁体   English   中英

Python中的类级别只读属性

[英]Class-level read-only properties in Python

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

x = Foo.CLASS_PROPERTY

但是阻止任何人说:

Foo.CLASS_PROPERTY = y

编辑:我喜欢Alex Martelli解决方案的简单性,但不喜欢它需要的语法。 他和~ututbu的答案都启发了以下解决方案,这更接近我所寻找的精神:

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不像普通值那样“表现”。 例如,我无法将其用作类中方法的参数的默认值。

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

>>> 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
>>> 

这实际上要简单得多,因为它只是基于这样一个事实:当你获得一个实例时,属性描述符会在类上查找(当然,当你得到一个类的属性描述符被查看在元类上时),并且创建类/元类独特并不是特别难。

哦,当然:

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

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

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

根据Raymond Hettinger的评论 ,这是一种方式:

#!/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

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()

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM