2009-11-14 115 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 

只是它確實是只讀的確認!