1. 程式人生 > >python類方法中的self關鍵字

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即是表示例項化物件的形參。