通常,方法在繫結後立即被呼叫:
在 myclass 示例中,這將返回字串 'hello world'。 但是,立即呼叫乙個方法並不是必須的: x.f 是乙個方法物件,它可以被儲存起來以後再呼叫。 例如:
while true:
print(xf())
將繼續列印 hello world,直到結束。
當乙個方法被呼叫時到底發生了什麼? 你可能已經注意到上面呼叫 x.f() 時並沒有帶引數,雖然 f() 的函式定義指定了乙個引數。 這個引數發生了什麼事? 當不帶引數地呼叫乙個需要引數的函式時 python肯定會引發異常 --- 即使引數實際未被使用...
實際上,你可能已經猜到了答案:方法的特殊之處就在於例項物件會作為函式的第乙個引數被傳入。 在我們的示例中,呼叫 x.f() 其實就相當於 myclass.f(x)。 總之,呼叫乙個具有 n 個引數的方法就相當於呼叫再多乙個引數的對應函式,這個引數值為方法所屬例項物件,位置在其他引數之前。
如果你仍然無法理解方法的運作原理,那麼檢視實現細節可能會澄清問題。 當乙個例項的非資料屬性被引用時,將搜尋例項所屬的類。 如果名稱表示乙個屬於函式物件的有效類屬性,會通過合併打包(指向)例項物件和函式物件到乙個抽象物件中的方式來建立乙個方法物件:這個抽象物件就是方法物件。 當附帶引數列表呼叫方法物件時,將基於例項物件和引數列表構建乙個新的引數列表,並使用這個新引數列表呼叫相應的函式物件。
類和例項變數
一般來說,例項變數用於每個例項的唯一資料,而類變數用於類的所有例項共享的屬性和方法:
kind = 'canine' # class variable shared by all instances
def __init__(self, name):
self.name = name # instance variable unique to each instance
>>> d = dog('fido')
>>> e = dog('buddy')
>>> d.kind # shared by all dogs
'canine'
>>> e.kind # shared by all dogs
'canine'
>>> d.name # unique to d
'fido'
>>> e.name # unique to e
'buddy'
正如 名稱和物件 中已討論過的,共享資料可能在涉及 mutable 物件例如列表和字典的時候導致令人驚訝的結果。 例如以下**中的 tricks 列表不應該被用作python類變數,因為所有的 dog 例項將只共享乙個單獨的列表:
tricks = # mistaken use of a class variable
def __init__(self, name):
self.name = name
def add_trick(self, trick):
>>> d = dog('fido')
>>> e = dog('buddy')
>>> d.add_trick('roll over')
>>> e.add_trick('play dead')
>>> d.tricks # unexpectedly shared by all dogs
['roll over', 'play dead']
正確的類設計應該使用例項變數:
def __init__(self, name):
self.name = name
self.tricks = # creates a new empty list for each dog
def add_trick(self, trick):
>>> d = dog('fido')
>>> e = dog('buddy')
>>> d.add_trick('roll over')
>>> e.add_trick('play dead')
>>> d.tricks
['roll over']
>>> e.tricks
['play dead']
補充說明
資料屬性會覆蓋掉具有相同名稱的方法屬性;為了避免會在大型程式中導致難以發現的錯誤的意外名稱衝突,明智的做法是使用某種約定來最小化衝突的發生機率。 可能的約定包括方法名稱使用大寫字母,屬性名稱加上獨特的短字串字首(或許只加乙個下劃線),或者是用動詞來命名方法,而用名詞來命名資料屬性。
資料屬性可以被方法以及乙個物件的普通使用者(「客戶端」)所引用。 換句話說,類不能用於實現純抽象資料型別。 實際上,在 python 中沒有任何東西能強制隱藏資料 --- 它是完全基於約定的。 (而在另一方面,用 c 語言編寫的 python 實現則可以完全隱藏實現細節,並在必要時控制物件的訪問;此特性可以通過用 c 編寫 python 擴充套件來使用。)
客戶端應當謹慎地使用資料屬性 --- 客戶端可能通過直接運算元據屬性的方式破壞由方法所維護的固定變數。 請注意客戶端可以向乙個例項物件新增他們自己的資料屬性而不會影響方法的可用性,只要保證避免名稱衝突 --- 再次提醒,在此使用命名約定可以省去許多令人頭痛的麻煩。
在方法內部引用資料屬性(或其他方法!)並沒有簡便方式。 我發現這實際上提公升了方法的可讀性:當瀏覽乙個方法**時,不會存在混淆區域性變數和例項變數的機會。
方法的第乙個引數常常被命名為 self。 這也不過就是乙個約定: self 這一名稱在 python 中絕對沒有特殊含義。 但是要注意,不遵循此約定會使得你的**對其他 python 程式設計師來說缺乏可讀性,而且也可以想像乙個 類瀏覽器 程式的編寫可能會依賴於這樣的約定。
任何乙個作為類屬性的函式都為該類的例項定義了乙個相應方法。 函式定義的文字並非必須包含於類定義之內:將乙個函式物件賦值給乙個區域性變數也是可以的。 例如:
def f1(self, x, y):
return min(x, x+y)
class c:
f = f1
def g(self):
return 'hello world'
h = g
現在 f, g 和 h 都是 c 類的引用函式物件的屬性,因而它們就都是 c 的例項的方法 --- 其中 h 完全等同於 g。 但請注意,本示例的做法通常只會令程式的閱讀者感到迷惑。
方法可以通過使用 self 引數的方法屬性呼叫其他方法:
def __init__(self):
self.data =
def add(self, x):
def addtwice(self, x):
self.add(x)
self.add(x)
方法可以通過與普通函式相同的方式引用全域性名稱。 與方法相關聯的全域性作用域就是包含其定義的模組。 (類永遠不會被作為全域性作用域。) 雖然我們很少會有充分的理由在方法中使用全域性作用域,但全域性作用域存在許多合法的使用場景:舉個例子,匯入到全域性作用域的函式和模組可以被方法所使用,在其中定義的函式和類也一樣。 通常,包含該方法的類本身是在全域性作用域中定義的,而在下一節中我們將會發現為何方法需要引用其所屬類的很好的理由。
每個值都是乙個物件,因此具有 類 (也稱為 型別),並儲存為 object.__class__ 。
python類物件和例項物件
類物件支援兩種操作 屬性引用和例項化。屬性引用 使用 python 中所有屬性引用所使用的標準語法 obj.name。有效的屬性名稱是類物件被建立時存在於類命名空間中的所有名稱。因此,如果類定義是這樣的 a example class i 12345 def f self return hello ...
例項方法,類方法和物件方法
class a num 10 def hehe self print 我是例項方法 也叫物件方法 classmethod def haha cls print 我是類方法,我的第乙個引數代表的是類,例如本例是a cls.num staticmethod def heihei print 我是靜態方法...
關於Python類 方法 物件 例項 靜態方法
類裡面的方法有3種 類的例項方法 平時常用的帶有self的方法 靜態方法 staticmethod 類方法 classmethod 這裡主要看一下類的例項方法和靜態方法的區別 1 例項方法只能被例項物件呼叫,第乙個引數必須要預設傳例項物件,一般習慣用self 2 靜態方法 由 staticmetho...