python類方法中的self關鍵字
之前學python爬蟲的時候要把函式封裝到類裡面,寫成類方法,知道在python的類方法中第一個引數應該是self,但對self代表的具體意義不甚了了。最近在看Java,對面向物件程式設計的瞭解更多了一點,終於徹底弄明白self到底是什麼了。
Python的類
在python中,所有的類都直接或間接繼承自Object類,定義了類之後就定義了一個名稱空間,裡面定義的屬性可以通過類名來引用。新定義的類中有一些Object中有的屬性,可以在其中定義其他變數或者函式。例項化之後會建立一個物件,該物件脫胎於類,並且其中的屬性動態地和類中的屬性產生關聯:
class A:
pass
a = A()
這段程式碼建立了一個類A,並且對它進行了例項化,例項化之後的物件繫結為變數a。我可以看看A裡面和a裡面分別有什麼:
In [38]: dir(A)
Out[38]:
['__class__',
'__delattr__',
'__dict__',
'__dir__',
'__doc__',
'__eq__',
'__format__',
'__ge__',
'__getattribute__',
'__gt__',
'__hash__',
'__init__',
'__init_subclass__',
'__le__',
'__lt__',
'__module__' ,
'__ne__',
'__new__',
'__reduce__',
'__reduce_ex__',
'__repr__',
'__setattr__',
'__sizeof__',
'__str__',
'__subclasshook__',
'__weakref__']
In [39]: dir(a)
Out[39]:
['__class__',
'__delattr__',
'__dict__',
'__dir__',
'__doc__',
'__eq__',
'__format__',
'__ge__',
'__getattribute__' ,
'__gt__',
'__hash__',
'__init__',
'__init_subclass__',
'__le__',
'__lt__',
'__module__',
'__ne__',
'__new__',
'__reduce__',
'__reduce_ex__',
'__repr__',
'__setattr__',
'__sizeof__',
'__str__',
'__subclasshook__',
'__weakref__']
用dir函式輸出A中和a中的屬性後,我們可以看到A和a中的屬性是一樣的。我們可以向其中新增屬性,新增屬性時必須初始化:
In [40]: A.b
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-40-ebcfc7dbf31a> in <module>()
----> 1 A.b
AttributeError: type object 'A' has no attribute 'b'
In [41]: A.b = 1
現在我們可以看看A和a中的屬性發生了什麼變化:
In [42]: dir(A)
Out[42]:
['__class__',
'__delattr__',
'__dict__',
'__dir__',
'__doc__',
'__eq__',
'__format__',
'__ge__',
'__getattribute__',
'__gt__',
'__hash__',
'__init__',
'__init_subclass__',
'__le__',
'__lt__',
'__module__',
'__ne__',
'__new__',
'__reduce__',
'__reduce_ex__',
'__repr__',
'__setattr__',
'__sizeof__',
'__str__',
'__subclasshook__',
'__weakref__',
'b']
In [43]: dir(a)
Out[43]:
['__class__',
'__delattr__',
'__dict__',
'__dir__',
'__doc__',
'__eq__',
'__format__',
'__ge__',
'__getattribute__',
'__gt__',
'__hash__',
'__init__',
'__init_subclass__',
'__le__',
'__lt__',
'__module__',
'__ne__',
'__new__',
'__reduce__',
'__reduce_ex__',
'__repr__',
'__setattr__',
'__sizeof__',
'__str__',
'__subclasshook__',
'__weakref__',
'b']
In [74]: A.b
Out[74]: 1
In [75]: a.b
Out[75]: 1
我們可以看到,在A中和它的例項化物件中現在都有了屬性b,而且它們的值相等。
如果我們給A的例項化物件中新增屬性呢:
In [44]: a.c = 2
In [45]: hasattr(a, c)
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
<ipython-input-45-15d927c71e90> in <module>()
----> 1 hasattr(a, c)
NameError: name 'c' is not defined
竟然報錯了,c沒有定義,報錯的原因是A的例項化物件a的名稱空間中有c,但是公共名稱空間中沒有c,所以我們再試一次:
In [58]: c = a.c
In [59]: hasattr(a, 'c')
Out[59]: True
In [60]: hasattr(A, 'c')
Out[60]: False
我們可以看到,a中有c,但是A中並沒有c。是不是因為c指向的是a.c所以A中沒有呢:
In [61]: b = a.b
In [62]: hasattr(A, 'b')
Out[62]: True
確實是因為在類的例項化物件中新增的屬性不會加入類中。
我們接著看給A或a中加入函式屬性會發生什麼:
In [78]: A.foo = lambda x : x + 1
In [79]: A.foo(1)
Out[79]: 2
In [80]: a.foo(1)
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-80-7dc85fd7a693> in <module>()
----> 1 a.foo(1)
TypeError: <lambda>() takes 1 positional argument but 2 were given
python方法函式中的self關鍵字
上面執行”a.foo(1)”語句時有個報錯,說只需要一個引數,但是給了兩個引數,這第二個引數是怎麼來的,為什麼A.foo(1)就不會出錯。這裡我們可以引出python類中的方法函式,方法函式指的是通過類的例項化物件呼叫的函式,方法函式的第一個形參表示類的例項化物件,通常寫成self。執行a.foo(1)時就相當於執行A.foo(a,1),因為A.foo()中只有一個形參,所有傳入的引數大於需要的引數,發生型別錯誤。
我們在A的定義中重新定義foo:
class A:
def foo(self, n):
print(n+1)
a = A()
現在我們在a中呼叫foo就不會有問題了:
In [85]: a.foo(1)
2
我們也可以試試呼叫A.foo:
In [86]: A.foo(a, 1)
2
總結
python的類中定義函式時的self關鍵字跟python的方法函式有關,方法函式由類的例項化物件呼叫,需要把呼叫它的例項化物件傳入方法函式中,self即是表示例項化物件的形參。