2009-11-14 87 views
25

是否有某种方法可以在Python中创建类级别的只读属性?举例来说,如果我有一个类Foo,我想说:Python中的类级别只读属性

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 s不“行为”像朴素的价值观。例如,我无法将它用作类中某个方法的参数的默认值。
+2

为什么有人会尝试设置Foo.CLASS_PROPERTY? Python中的典型风格是在规划时选择简单性,以防止某个人做某些愚蠢的理论实例。你无法做任何事情来阻止人们用你的代码来做愚蠢的事情。 – 2009-11-14 23:15:27

+0

@Mike:你是对的;常识会告诉人们不要修改这些属性。然而,偶尔他们会被修改。这就是为什么我喜欢它被拼写出来(比全部大写更明确)。与使用私人会员的理由相同。显式接口很好。不要误解我的意思:我也喜欢简单。我更喜欢如果有一些内置的设施(比如@classproperty装饰器或其他)。事实上,如果每次都需要设置'__metaclass__',我甚至不确定是否会使用上述解决方案。现在,全部大写可能会赢。 – mjumbewu 2009-11-15 00:07:22

+2

未来人们的注意事项:现代python版本可以在新式类中使用'@ property':http://docs.python.org/library/functions.html#property – 2012-06-07 15:12:35

回答

1

发现这对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

+0

这适用于实例级属性,但不适用于(即我仍然不能说'ROClass.a',并期望值为'1')。 – mjumbewu 2009-11-14 20:59:18

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 

的想法是这样的:首先考虑雷蒙德赫廷杰的解决方案:

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

它显示了一个比较简单的方法,让巴读 - 只有财产。

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

所以,如果你想在类Foo有一个只读属性,然后父类的Foo必须有CLASS_PROPERTY = property(lambda self: 1)定义。

一个类的父类是一个元类。因此我们定义ROType作为元类:

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

然后我们做Foo的父类是ROType:

class Foo(object): 
    __metaclass__=ROType 
+0

现在我觉得自己像一个白痴,没有仔细阅读评论>。< – 2009-11-14 21:26:17

8

现有的解决方案是一个有点复杂 - 大约只保证什么,在一定的各班组具有唯一的元类,然后在自定义元类上设置正常的只读属性。即:

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

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