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