2014-09-26 19 views
7

我一直在尋找在這個問題的答案: Is it possible to define a class constant inside an Enum?的Python:恆類

最讓我感興趣的是恆類伊桑·弗曼的答案。

class Constant: 
    def __init__(self, value): 
     self.value = value 
    def __get__(self, *args): 
     return self.value 
    def __repr__(self): 
     return '%s(%r)' % (self.__class__.__name__, self.value) 

問題是關於Python 3.4,但我使用的是2.7。在回答伊桑設置引力常數作爲類星球,像這樣的實例變量:

G = Constant(6.67300E-11) 

我在2.7這個類的測試表明,僅僅鍵入摹給了我這樣的:

Out[49]: Constant(3) 

(我將它設置爲3,以便在測試時使用,看起來像__repr__輸出給我,如果我錯了,請糾正我的錯誤。)

該值可通過G.value。但在Ethan的回答中,他使用

return self.G * self.mass/(self.radius * self.radius) 

這顯然只適用於如果返回值與__repr__輸出。現在,如果我將class Constant:更改爲class Constant(int):然後鍵入G我仍然得到__repr__輸出,但如果我輸入G * 4我得到12而不是我得到的錯誤。 (TypeError:不支持的操作數類型爲*:'instance'和'int' )

很明顯,像int對象這樣的東西在調用時可以輸出一個數字。是否有一種我錯過的神奇方法可以讓我爲Constant類做到這一點?由於常量可以是字符串,整數或浮點數,所以我希望有1個類可以處理它們,而3個類可以擴展這些對象。

該值也可以通過G.value設置。我可以鎖定這個常量類的行爲是否像一個實際的常量? (我懷疑答案是否定的。)

+0

爲什麼你希望它是恆定的? – Veedrac 2014-09-27 05:48:39

+0

@Veedrac爲什麼我不想要常量?或者只讀取一個班級中的值?從我對Python的閱讀中,我瞭解到它與Python的「我們都是自願的成年人」哲學背道而馳。我到目前爲止真的很喜歡Python,但我認爲哲學是一個疏忽。我爲我,我自己和我編程 - 沒有人會閱讀它或者很有可能使用它。在過去的15年裏用幾種語言編寫代碼後,我學到了一些東西。其中一件事就是我可以通過簡單地設計一個類,在那個類的內部之外不能改變某些東西,從而防止幾天的調試。 – 2014-09-27 14:24:38

+0

我更想知道你需要什麼水平才能保持不變;我的觀點是檢查是否「通過公共接口不變」,這是這樣做的正確方法是確定了,或者你確實希望它是*完全*不變,這是不可能的。 – Veedrac 2014-09-27 14:29:02

回答

2

你的類常量應該從對象繼承,成爲一個新的Python類風格。

那樣Constant就是所謂的描述符。簡而言之,描述符是一種Python結構,用於定製獲取和設置類屬性的行爲。當描述符的實例被設置爲另一個類的屬性時,它們非常有用。

在你的例子中,常量是描述符,而Planet有一個屬性,它是Constant的一個實例。當你得到Planet類的屬性G(例如self.G)時,你真正得到的是描述符的__get__方法返回的值,即值。

請注意,__get__僅在描述符實例被另一個類屬性訪問時調用。

所以,定義類是這樣的:

class Constant(object): 
    def __init__(self, value): 
     self.value = value 
    def __get__(self, *args): 
     return self.value 
    def __repr__(self): 
     return '%s(%r)' % (self.__class__.__name__, self.value) 

然後這個小例子:

c = Constant(3.14) 
print c 

class Test: 
    c = Constant(3.14) 

t = Test() 
print t.c 

會打印:

Constant(3.14) 
3.14 

看到,當恆實例直接打印,__repr__方法將被調用,但當作爲另一個類屬性打印時,__get__將被使用。

你可以閱讀更多的描述上this great article

+0

從我測試過,如果它是一個類變量而不是一個實例變量,它纔會起作用。如果我通過'self.c = Constant(...)'將'c = Constant'移動到'__init__',那麼我會得到'__repr__'輸出。它仍然是可寫的,對於常量來說是很好的,因爲我可以用所有CAPS命名。理想情況下,我希望找到一種方法來創建一個實例變量,可以從類中寫入,但不能寫入'__main__'腳本之外。我在PHP中做了一些很好的工作,但從我讀過的內容來看,它在Python中是不可能的。 class屬性似乎總是​​可用於覆蓋。謝謝 – 2014-09-27 00:06:56

0

那麼,value是你的class Constant的memeber;所以你可以嘗試making it private

class Constant: 
    def __init__(self, value): 
    # This actually transforms the variable to _Constant__value, 
    # but also hides it from outer scope 
    self.__value = value 
    def __get__(self, *args): 
    # Altough the member is theorically renamed as _Constant__value, 
    # it is completely accesible from inside the class as __value 
    reurn self.__value 
    def __repr__(self): 
    return '%s(%r)' % (self.__class__.__name__, self.__value) 

另一種方法可以以下this recipe

給他們一個嘗試,讓我現在。希望我幫了你!

+0

我還沒有嘗試配方,但你提供的代碼並不適合我,因爲它是自己的實例。我無法直接獲得價值。我懷疑這是因爲@ baxeico說過它必須成爲課堂上的描述符。 Thx – 2014-09-27 00:08:56

+0

配方似乎工作。但是,它必須駐留在文件中並被導入。它也似乎是一個單身人士。在類中導入它(僅在類定義下面)並不會將其限制爲該類的名稱空間。也不會將輸入行放在'__init__'中。如果你能忍受這些限制,配方工作得非常好。 – 2014-09-29 19:29:09