溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

Python中的self參數(shù)怎么使用

發(fā)布時(shí)間:2023-05-09 10:52:43 來源:億速云 閱讀:87 作者:iii 欄目:編程語言

今天小編給大家分享一下Python中的self參數(shù)怎么使用的相關(guān)知識(shí)點(diǎn),內(nèi)容詳細(xì),邏輯清晰,相信大部分人都還太了解這方面的知識(shí),所以分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后有所收獲,下面我們一起來了解一下吧。

讓我們從我們已經(jīng)知道的開始:self - 方法中的第一個(gè)參數(shù) - 指的是類實(shí)例:

class MyClass:
┌─────────────────┐
▼ │
def do_stuff(self, some_arg): │
print(some_arg)▲│
 ││
 ││
 ││
 ││
instance = MyClass() ││
instance.do_stuff("whatever") │
│ │
└───────────────────────────────┘

此外,這個(gè)論點(diǎn)實(shí)際上不必稱為 self - 它只是一個(gè)約定。例如,你可以像其他語言中常見的那樣使用它。

上面的代碼可能是自然而明顯的,因?yàn)槟阋恢痹谑褂茫俏覀冎唤o了 .do_stuff() 一個(gè)參數(shù) (some_arg),但該方法聲明了兩個(gè) (self 和 , some_arg),好像也說不通。片段中的箭頭顯示 self 被翻譯成實(shí)例,但它是如何真正傳遞的呢?

instance = MyClass()
MyClass.do_stuff(instance, "whatever")

Python 在內(nèi)部所做的是將 instance.do_stuff("whatever") 轉(zhuǎn)換為 MyClass.do_stuff(instance, "whatever")。我們可以在這里稱之為“Python 魔法”,但如果我們想真正了解幕后發(fā)生的事情,我們需要了解 Python 方法是什么以及它們與函數(shù)的關(guān)系。

類屬性/方法

在 Python 中,沒有“方法”對(duì)象之類的東西——實(shí)際上方法只是常規(guī)函數(shù)。函數(shù)和方法之間的區(qū)別在于,方法是在類的命名空間中定義的,使它們成為該類的屬性。

這些屬性存儲(chǔ)在類字典 __dict__ 中,我們可以直接訪問或使用 vars 內(nèi)置函數(shù)訪問:

MyClass.__dict__["do_stuff"]
#vars(MyClass)["do_stuff"]
#

訪問它們的最常見方法是“類方法”方式:

print(MyClass.do_stuff)
#

在這里,我們使用類屬性訪問該函數(shù),正如預(yù)期的那樣打印 do_stuff 是 MyClass 的函數(shù)。然而,我們也可以使用實(shí)例屬性訪問它:

print(instance.do_stuff)
#<bound method MyClass.do_stuff of

但在這種情況下,我們得到的是一個(gè)“綁定方法”而不是原始函數(shù)。Python 在這里為我們所做的是,它將類屬性綁定到實(shí)例,創(chuàng)建了所謂的“綁定方法”。這個(gè)“綁定方法”是底層函數(shù)的包裝,該函數(shù)已經(jīng)將實(shí)例作為第一個(gè)參數(shù)(self)插入。

因此,方法是普通函數(shù),它們的其他參數(shù)前附加了類實(shí)例(self)。

要了解這是如何發(fā)生的,我們需要看一下描述符協(xié)議。

描述符協(xié)議

描述符是方法背后的機(jī)制,它們是定義 __get__()、__set__() 或 __delete__() 方法的對(duì)象(類)。為了理解 self 是如何工作的,我們只考慮 __get__(),它有一個(gè)簽名:

descr.__get__(self, instance, type=None) -> value

但是 __get__() 方法實(shí)際上做了什么?它允許我們自定義類中的屬性查找 - 或者換句話說 - 自定義使用點(diǎn)符號(hào)訪問類屬性時(shí)發(fā)生的情況??紤]到方法實(shí)際上只是類的屬性,這非常有用。這意味著我們可以使用 __get__ 方法來創(chuàng)建一個(gè)類的“綁定方法”。

為了讓它更容易理解,讓我們通過使用描述符實(shí)現(xiàn)一個(gè)“方法”來演示這一點(diǎn)。首先,我們創(chuàng)建一個(gè)函數(shù)對(duì)象的純 Python 實(shí)現(xiàn):

import types
class Function:
def __get__(self, instance, objtype=None):
if instance is None:
return self
return types.MethodType(self, instance)
def __call__(self):
return

上面的 Function 類實(shí)現(xiàn)了 __get__ ,這使它成為一個(gè)描述符。這個(gè)特殊方法在實(shí)例參數(shù)中接收類實(shí)例 - 如果這個(gè)參數(shù)是 None,我們知道 __get__ 方法是直接從一個(gè)類(例如 MyClass.do_stuff)調(diào)用的,所以我們只返回 self。但是,如果它是從類實(shí)例中調(diào)用的,例如 instance.do_stuff,那么我們返回 types.MethodType,這是一種手動(dòng)創(chuàng)建“綁定方法”的方式。

此外,我們還提供了 __call__ 特殊方法。__init__ 是在調(diào)用類來初始化實(shí)例時(shí)調(diào)用的(例如 instance = MyClass()),而 __call__ 是在調(diào)用實(shí)例時(shí)調(diào)用的(例如 instance())。我們需要用這個(gè),是因?yàn)?types.MethodType(self, instance) 中的 self 必須是可調(diào)用的。

現(xiàn)在我們有了自己的函數(shù)實(shí)現(xiàn),我們可以使用它將方法綁定到類:

class MyClass:
do_stuff = Function()
print(MyClass.__dict__["do_stuff"])# __get__ not invoked
#print(MyClass.do_stuff)# __get__ invoked, but "instance" is None, "self" is returned
print(MyClass.do_stuff.__get__(None, MyClass))
#instance = MyClass()
print(instance.do_stuff)#__get__ invoked and "instance" is not None, "MethodType" is returned
print(instance.do_stuff.__get__(instance, MyClass))
#<bound method ? of

通過給 MyClass 一個(gè) Function 類型的屬性 do_stuff,我們大致模擬了 Python 在類的命名空間中定義方法時(shí)所做的事情。

綜上所述,在instance.do_stuff等屬性訪問時(shí),do_stuff在instance的屬性字典(__dict__)中查找。如果 do_stuff 定義了 __get__ 方法,則調(diào)用 do_stuff.__get__ ,最終調(diào)用:

# For class invocation:
print(MyClass.__dict__['do_stuff'].__get__(None, MyClass))
## For instance invocation:
print(MyClass.__dict__['do_stuff'].__get__(instance, MyClass))
# Alternatively:
print(type(instance).__dict__['do_stuff'].__get__(instance, type(instance)))
#<bound method ? of

正如我們現(xiàn)在所知 - 將返回一個(gè)綁定方法 - 一個(gè)圍繞原始函數(shù)的可調(diào)用包裝器,它的參數(shù)前面有 self !

如果想進(jìn)一步探索這一點(diǎn),可以類似地實(shí)現(xiàn)靜態(tài)和類方法

為什么self在方法定義中?

我們現(xiàn)在知道它是如何工作的,但還有一個(gè)更哲學(xué)的問題——“為什么它必須出現(xiàn)在方法定義中?”

顯式 self 方法參數(shù)是有爭(zhēng)議的設(shè)計(jì)選擇,但它是一種有利于簡(jiǎn)單性的選擇。

Python 的自我體現(xiàn)了“越差越好”的設(shè)計(jì)理念——在此處進(jìn)行了描述。這種設(shè)計(jì)理念的優(yōu)先級(jí)是“簡(jiǎn)單”,定義為:

設(shè)計(jì)必須簡(jiǎn)單,包括實(shí)現(xiàn)和接口。實(shí)現(xiàn)比接口簡(jiǎn)單更重要...

這正是 self 的情況——一個(gè)簡(jiǎn)單的實(shí)現(xiàn),以接口為代價(jià),其中方法簽名與其調(diào)用不匹配。

Python 抽象了很多復(fù)雜性,但在我看來,深入研究低級(jí)細(xì)節(jié)和復(fù)雜性對(duì)于更好地理解該語言的工作原理非常有價(jià)值,當(dāng)事情發(fā)生故障和高級(jí)故障排除/調(diào)試時(shí),它可以派上用場(chǎng)不夠。

此外,理解描述符實(shí)際上可能非常實(shí)用,因?yàn)樗鼈冇幸恍┯美km然大多數(shù)時(shí)候你真的只需要@property 描述符,但在某些情況下自定義的描述符是有意義的,例如 SLQAlchemy 中的或者 e.g.自定義驗(yàn)證器。

以上就是“Python中的self參數(shù)怎么使用”這篇文章的所有內(nèi)容,感謝各位的閱讀!相信大家閱讀完這篇文章都有很大的收獲,小編每天都會(huì)為大家更新不同的知識(shí),如果還想學(xué)習(xí)更多的知識(shí),請(qǐng)關(guān)注億速云行業(yè)資訊頻道。

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI