2013-05-25 67 views
2

首先,我來自java和php背景,現在正在學習python。這可能因此看起來像一個愚蠢的問題,但它讓我有點困惑。Python類成員混淆

作爲在PHP一個例子:

class MyClass 
{ 
    private $myMember; 

    public function __construct($myMember = null) 
    { 
     $this->myMember = $myMember; 
    } 

    public function setMyMember($myMember) 
    { 
     $this->myMember = $myMember; 
    } 

    public function getMyMember() 
    { 
     return $this->myMember; 
    } 

} 

這是完全教科書101 OOP。一個單獨的私人成員和一個getter和setter的類。

然而,學習一些Python這不是那麼簡單,似乎。

首先,構造函數是不是一個真正的構造函數(但你可以在這樣的使用?)

其次,任何一流水平的減速是靜態處理?那麼python中的self.myMember實際上在實例上設置了一個不同於類級別myMember的實例的不同變量?

最後,如果我的困惑是正確的 - 如何將同樣的例子之上Python編寫?

回答

4

一般情況下,你不要在Python中使用getter和setter ;除非你需要改造的價值,需要的副作用期間設置或獲取,或者需要防止或者獲取或設置,你只需使用屬性來代替:

class MyClass(object): 
    def __init__(self, member): 
     self.member = member 

實例屬性是分開的類屬性;在self上設置屬性意味着您正在操作實例,類(MyClass.member)上的屬性是類屬性,因此在實例中共享。這是通過在查找屬性時將類用作回退來實現的;首先查詢實例,然後查詢實例的類,然後查看該類的基類;設置self.member意味着任何MyClass.member在實例上不再可見。

__init__方法的確,在技術上,而不是一個構造函數。當調用__init__時,self已經存在。它是一個初始化程序,您可以用它設置第一個值。如果您需要實際構造函數(實例工廠),則需要改爲查找__new__ static method。請注意,在PHP中,__construct也是一個初始化程序,而不是構造函數!

對於這些情況下,您有需要創建getter和setter方法,使用property decorator function

class MyClass(object): 
    def __init__(self, member): 
     self._member = member 

    @property 
    def member(self): 
     return 'Hello {}!'.format(self._member) 

    @member.setter 
    def member(self, value): 
     # Remove "Hello " and "!" from the start and end of any value being set. 
     if value.startswith('Hello '): 
      value = value.split(None, 1)[1] 
     self._member = value.rstrip('!') 

演示:

>>> m = MyClass('World') 
>>> m.member 
'Hello World!' 
>>> m.member = 'Hello Planet Earth!' 
>>> m.member 
'Hello Planet Earth!' 
>>> m._member 
'Planet Earth' 
+2

好吧,'__init__'是一個構造函數,就像PHP的'__construct'是一個構造函數一樣。無論您是否同意這個術語,'__new__'是其他語言稱爲工廠的東西(有些語言支持可以方便地調用它),並且它比其他語言稱爲構造函數的功能嚴格得多(可以返回不同或已有的對象) 。所以,OP:只需使用'__init__',句號。 – delnan

+0

在我有經驗的每種* OO語言中,您編寫的用於初始化對象並稱爲構造函數的代碼塊將在已存在的對象上調用。因此,如果Python和PHP構造函數「不是真正的構造函數」,那麼Java或C++構造函數都不是。 – Ben

+0

爲什麼你要將'__new__'作爲靜態方法?我傾向於認爲它是一個類方法,因爲第一個參數是一個類... – mgilson

3

這將是這樣的:

class MyClass(object): 

    def __init__(self, myMember=None): 
     self._myMember = myMember 

    @property 
    def myMember(self): 
     return self._myMember 

    @myMember.setter 
    def myMember(self, value): 
     self._myMember = value 

演示:

c = MyClass() 
c.myMember = 10 
print c.myMember 

而且你必須:

# 10 
+1

雙下劃線最初是不合適在這裏。 –

+0

謝謝我解決了! –

1

在Python中,類創建一個新的命名空間。除了有趣的商業(如元類)之外,名稱空間中的東西成爲上的屬性。

class Foo(object): #inherit from object in python2.x ... It's a good idea 
    class_variable = 3 
    __not_really_private_but_sort_of = 7 

現在我創建了一個具有2個屬性的類。第一個是「公開」。第二個是「私人」,它的前綴是雙下劃線,它會調用python的名稱。在Python中,這是很少使用的。畢竟,我們都在這裏同意程序員:)。

當你調用一個,它調用__new__方法(構造函數),然後(如果__new__返回類的實例),它會調用初始化__init__)。大多數時候,你不需要做任何事情__new__ - 默認__new__做得很好。由於__init__是一個普通的方法,它臨危類作爲第一個參數的實例 - 基本上填充其他語言this->指針的作用:

class Foo(object): 
    member = 4 
    other_member = 5 
    def __init__(self): 
     print "__init__ called!" 
     self.member = 3 
     print self.member 
     print self.other_member 

有一件事情是有點怪約蟒蛇相比,其他語言是這樣一個事實,如果一個實例沒有成員,python然後查找上的成員,然後查找基類(按照方法解析順序搜索)。如果它沒有在任何地方找到該成員,則會產生一個AttributeError。所以,如果我實例化了我上面的類,它會打印:

__init__ called! 
3 
5 

另外請注意,我可以通過類訪問類(「靜態」你叫吧)成員:

print Foo.member 

或通過一個實例:

print Foo().__class__.member 

至於你會怎麼寫上面的PHP在Python ...答案是,你不會的。這將是最好寫這樣的:

class MyClass(object): 
    def __init__(self,mymember=None): 
     self.mymember = mymember 

實在沒有很好的理由有二傳,其唯一目的是設置一個私有成員。如果你希望用戶有能力重置mymember屬性,那麼習慣的方式是讓他們直接訪問它,而不是強迫他們調用一個函數來完成這項工作。如果您發現您確實需要該功能,則可以隨後將mymember置於某個屬性中(如其他一些答案中所述)。

+0

'__new__'不是構造函數,'__init__'絕對是。 '__new__'在Java/C++中做了一些你不能做的事情(控制對象是如何分配的)。 Java/C++構造函數對已經存在的對象進行操作,並提供一個鉤子來初始化對象的狀態 - 「__init__」確實是這樣做的。 – Ben

+0

@Ben - 最終歸結爲語義。 '__new__'是負責構建新對象的方法。當你通過調用一個類來構造一個實例時,它也是第一個被調用的東西。因此,我認爲它是構造函數,我想'__init__'和「初始化器」。我真的不關心在Java或C++中沒有平行的東西......當然,這是我的2美分,我一次又一次看到了這個爭論。最終,它只是語義學,重要的是理解語言如何工作,我希望我已經展示了...... – mgilson

+1

認真地說,我認爲這種態度對新學習者的理解是有害的(只是略微,但仍然)。見證OP的困惑!術語「構造函數」已經存在了幾十年,這正是「__init__」的意思。如果你不喜歡標準術語,只要將它們稱爲'__init__'和'__new__'(與C++/Java構造函數不同,在Python中它們總是具有相同的名稱,所以我們不需要另外一個術語)。不要隨意更改術語,然後根據C++構造函數不符合的標準「解釋」「__init__」不是構造函數。 – Ben

1

這裏有幾點你需要知道Python如何做面向對象,如果你來自Java/PHP。


在Python中,類也是非常普通的對象。這意味着類自己可以有成員變量(我們通常只是稱它們爲屬性)。您在類塊中分配的所有名稱都將成爲類本身的屬性,而不是其任何實例的屬性(包括創建方法的函數def塊;這就是Python中的所有方法)。

由於您通常有一個類和許多實例,類屬性可以爲Java類中的靜態變量提供類似的用途,但它們並不完全相同。

在類塊中分配名稱從不在類的實際屬性上創建屬性; 只有在對象(任何對象,類或普通實例)上創建屬性的方法是在其存在時分配它們。因此,Python的__init__方法中必須存在類的實例所有變量的標準聲明,並在self上指定屬性。

例如:

class Foo(object): 
    foo = "I am a class attribute" 

    def __init__(self): 
     self.bar = "I am an instance attribute" 

如果這往往混淆了新的Python程序員是試圖從一個實例將實際工作,並找到類屬性(這是方法調用如何找到foo屬性他們應該調用的函數)。所以它看起來像在類塊中聲明實例屬性,可以用於第一次休閒遊戲。但實際上只有一個屬性foo,在所有實例之間共享,如果它是可變對象並且您希望每個實例都有其自己的副本,則這可能非常糟糕。


在Python中沒有訪問控制,所以沒有私有變量這樣的事情。這是因爲Python具有足夠的動態性,以至於你總能繞過任何這樣的限制,使它們變得毫無意義。

相反,Python程序員傾向於依靠文檔和約定來確保編寫客戶端代碼的程序員知道哪些名稱是公共API的一部分,哪些是實現細節。然後,我們相信每個人都會主要遵守規則,那些不需要處理他們的代碼的人就是他們不應該依賴於變化的東西。

無論如何,在任何語言中,具有微不足道的getter和setter的屬性都是「公共」的;唯一可能的擔憂是你可能「有一天」想要改變getter/setter的實現,以便在不改變你的接口的情況下它們不再微不足道。 Python可以通過屬性來解決這個問題(基本上允許你進行方法調用,看起來像讀或寫屬性),所以絕對沒有理由擁有getter/setter方法,除非實際上有更多的事情發生。

因此,一般來說,如果期望客戶端代碼操縱對象的接口包括讀取和寫入字段,則只需記錄一個普通屬性可供讀取和寫入。如果你想清楚一個屬性(或方法)純粹是爲了內部使用,通常的慣例是用一個下劃線開始名稱。這會強制任何使用該名稱的人知道他們正在訪問內部名稱(因爲他們必須鍵入前導下劃線),所以您不必擔心有人會「意外」使用私有屬性。


關於構造,只是對待__init__方法__construct從PHP,它會做你所期望的(但一定要記住,Python方法明確收到self參數;沒有隱含this)。

有這個神話,__init__與構造函數有點不同,所以你不應該稱它爲一個。至於我可以告訴罪魁禍首是Dive Into Python,這相當奇怪的說:

這將是誘人的,但不正確調用這個 類的構造函數。這很誘人,因爲它看起來像一個構造函數(按照約定 約定,__init__是爲該類定義的第一個方法),行爲 就像一個(它是在該類的新創建的 實例中執行的第一段代碼),甚至聽起來像一個(「初始化」當然 建議一個構造函數性質)。不正確,因爲對象具有 的時間已被調用__init__被調用,並且您的 已具有對該類的新實例的有效引用。

但「對象已經由時間__init__構造被調用,並且你已經有了類的新實例的有效引用」精確表徵的構造是如何傳統的工作,所以我不知道什麼時候作者正試圖在那裏。

(該出現採取官方行是MyClass(arg1, arg2)構造表達; __init____new__都描述爲接收被賦予給構造表達式的所有參數,並且__init__明確地稱爲構造這兩個術語都很少使用的文檔;他們只是說__init__最重要的時間)

所以我勸你忽略人誰試圖告訴你,__init__不是構造函數。你將通過類比構造函數來理解__init__,而不是試圖理解他們在構造函數和__init__之間做出的關鍵區別。


把所有在一起,你的PHP類的成語翻譯將是簡單如下:

class MyClass(object): 
    def __init__(self, myMember): 
     self.myMember = myMember 
+0

我知道你在幾年前發佈過,但是我在研究Python OOP問題時偶然發現了這一點。這寫得非常好。感謝您發佈它。 – TMWP