中文字幕一区二区人妻电影,亚洲av无码一区二区乱子伦as ,亚洲精品无码永久在线观看,亚洲成aⅴ人片久青草影院按摩,亚洲黑人巨大videos

Python的實例、類和靜態(tài)方法被揭開神秘面紗

發(fā)布于:2021-02-01 13:56:20

0

84

0

python 靜態(tài)方法 實例

在本教程中,我將幫助解開類方法、靜態(tài)方法和常規(guī)實例方法背后的秘密。

如果您對它們之間的差異有了直觀的理解,您將能夠編寫面向?qū)ο蟮腜ython,它可以更清楚地傳達(dá)其意圖,并且從長遠(yuǎn)來看更易于維護(hù)。

實例,類,靜態(tài)方法-概述

讓我們從編寫一個(Python 3)類開始,該類包含所有三種方法類型的簡單示例:

class MyClass:     def method(self):         return 'instance method called', self     @classmethod     def classmethod(cls):         return 'class method called', cls     @staticmethod     def staticmethod():         return 'static method called'


注意:對于Python 2用戶:@staticmethod@classmethod修飾符從Python 2.4開始就可以使用了,這個示例將起作用照原樣。您可以選擇使用class MyClass(object):語法聲明從object繼承的新樣式類,而不是使用普通的class MyClass:聲明。除此之外,您還可以繼續(xù)使用

實例方法

MyClass調(diào)用的第一個方法method是常規(guī)實例方法。這是您大多數(shù)時候會使用的基本,簡潔的方法類型。您可以看到該方法self帶有一個參數(shù),它指向MyClass該方法何時被調(diào)用的實例(但當(dāng)然實例方法可以接受多個參數(shù))。

通過該self參數(shù),實例方法可以自由訪問同一對象上的屬性和其他方法。在修改對象的狀態(tài)時,這賦予了他們很多功能。

實例方法不僅可以修改對象狀態(tài),而且還可以通過self.__class__屬性訪問類本身。這意味著實例方法也可以修改類狀態(tài)。

類方法

讓我們將其與第二種方法進(jìn)行比較MyClass.classmethod。我用@classmethod裝飾器標(biāo)記了此方法,以將其標(biāo)記為類方法。

當(dāng)self調(diào)用方法時,類方法不接受參數(shù),而是采用cls指向類的參數(shù),而不是對象實例。

因為類方法只能訪問此cls參數(shù),所以它不能修改對象實例狀態(tài)。那將需要訪問self。但是,類方法仍然可以修改適用于該類所有實例的類狀態(tài)。

靜態(tài)方法

第三種方法,MyClass.staticmethod被標(biāo)記為一個@staticmethod裝飾器,以將其標(biāo)記為一個靜態(tài)方法。

這種類型的方法既不接受self也不接受cls參數(shù)(當(dāng)然它可以自由接受任意數(shù)量的其他參數(shù))。

因此靜態(tài)方法既不能修改對象狀態(tài),也不能修改類狀態(tài)。靜態(tài)方法在它們可以訪問的數(shù)據(jù)方面受到限制,它們主要是為方法命名命名空間的一種方法。

讓我們看看他們的行動!

我知道到目前為止,這一討論還只是理論上的。而且,我相信您必須對這些方法類型在實踐中的差異有一個直觀的了解。現(xiàn)在,我們將討論一些具體示例。

讓我們看一下這些方法在調(diào)用時的行為。我們將從創(chuàng)建該類的實例開始,然后在其上調(diào)用三個不同的方法。

MyClass 的設(shè)置方式是,每個方法的實現(xiàn)都返回一個元組,其中包含供我們跟蹤發(fā)生了什么的信息以及該方法可以訪問的類或?qū)ο蟮哪男┎糠帧?/span>

當(dāng)我們調(diào)用實例方法時,會發(fā)生以下情況:

>>> obj = MyClass() >>> obj.method() ('instance method called', <MyClass instance at 0x10205d190>)

這證實了method(實例方法)可以<MyClass instance>通過self參數(shù)訪問對象實例(打印為)。

調(diào)用該方法時,Python會將self參數(shù)替換為實例對象obj。我們可以忽略點調(diào)用語法(obj.method())的語法糖,并手動傳遞實例對象以獲得相同的結(jié)果:

>>> MyClass.method(obj) ('instance method called', <MyClass instance at 0x10205d190>)

您能猜出如果不先創(chuàng)建實例就嘗試調(diào)用該方法會發(fā)生什么情況嗎?

順便說一句,實例方法還可以通過屬性訪問類本身self.__class__。這使實例方法在訪問限制方面功能強大-它們可以修改對象實例和類本身的狀態(tài)。

接下來讓我們嘗試類方法:

>>> obj.classmethod() ('class method called', <class MyClass at 0x101a2f4c8>)

調(diào)用classmethod()顯示了它無權(quán)訪問該<MyClass instance>對象,而只能訪問<class MyClass>代表該類本身的對象(Python中的所有內(nèi)容都是一個對象,甚至是類本身)。

請注意,當(dāng)我們調(diào)用時,Python如何自動將類作為第一個參數(shù)傳遞給函數(shù)MyClass.classmethod()。通過點語法在Python中調(diào)用方法會觸發(fā)此行為。self實例方法上的參數(shù)以相同的方式工作。

請注意,命名這些參數(shù)self而cls僅僅是一個慣例。你可以很容易地為它們命名the_object和the_class和得到同樣的結(jié)果。重要的是它們在該方法的參數(shù)列表中排在第一位。

現(xiàn)在該調(diào)用靜態(tài)方法了:

>>> obj.staticmethod() 'static method called'

您是否看到我們?nèi)绾握{(diào)用staticmethod()對象并能夠成功完成調(diào)用?當(dāng)一些開發(fā)人員得知可以在對象實例上調(diào)用靜態(tài)方法時,他們會感到驚訝。

在幕后,Python只是通過使用點語法調(diào)用靜態(tài)方法時不傳遞self或cls參數(shù)來簡單地強制執(zhí)行訪問限制。

這證實了靜態(tài)方法既不能訪問對象實例狀態(tài)也不能訪問類狀態(tài)。它們像常規(guī)函數(shù)一樣工作,但屬于類(和每個實例的)名稱空間。

現(xiàn)在,讓我們看看嘗試在類本身上調(diào)用這些方法時發(fā)生的情況-無需事先創(chuàng)建對象實例:

>>> MyClass.classmethod() ('class method called', <class MyClass at 0x101a2f4c8>) >>> MyClass.staticmethod() 'static method called' >>> MyClass.method() TypeError: unbound method method() must         be called with MyClass instance as first      argument (got nothing instead)

我們能夠調(diào)用classmethod()并staticmethod()很好,但是嘗試調(diào)用實例方法method()失敗,并帶有TypeError。

這是可以預(yù)期的-這次我們沒有創(chuàng)建對象實例,而是嘗試直接在類藍(lán)圖本身上調(diào)用實例函數(shù)。這意味著Python無法填充self參數(shù),因此調(diào)用失敗。

這應(yīng)該使這三種方法類型之間的區(qū)別更加清晰。但是我不會再這樣了。在接下來的兩節(jié)中,我將介紹兩個更實際的示例,說明何時使用這些特殊方法類型。

我將基于這個簡單的Pizza類來舉例:

class Pizza:     def __init__(self, ingredients):         self.ingredients = ingredients     def __repr__(self):         return f'Pizza({self.ingredients!r})'
>>> Pizza(['cheese', 'tomatoes']) Pizza(['cheese', 'tomatoes'])

注意:此代碼示例以及本教程中的后續(xù)代碼示例使用Python 3.6 f-strings構(gòu)造由返回的字符串__repr__。在Python 2和3.6之前的Python 3版本上,您將使用不同的字符串格式表達(dá)式,例如:

def __repr__(self):     return 'Pizza(%r)' % self.ingredients

美味比薩餅工廠與@classmethod

如果你在現(xiàn)實世界中接觸過比薩餅,你會知道有許多美味的變化可用:

Pizza(['mozzarella', 'tomatoes'])
Pizza(['mozzarella', 'tomatoes', 'ham', 'mushrooms'])
Pizza(['mozzarella'] * 4)

意大利人幾個世紀(jì)前就想出了他們的比薩餅分類法,所以這些美味的比薩餅都有自己的名字。我們最好利用這個優(yōu)勢,為Pizza類的用戶提供一個更好的界面來創(chuàng)建他們想要的pizza對象。

一個好的、干凈的方法是使用類方法作為工廠函數(shù)來創(chuàng)建不同種類的pizza:

class Pizza:
    def __init__(self, ingredients):
        self.ingredients = ingredients

    def __repr__(self):
        return f'Pizza({self.ingredients!r})'

    @classmethod
    def margherita(cls):
        return cls(['mozzarella', 'tomatoes'])

    @classmethod
    def prosciutto(cls):
        return cls(['mozzarella', 'tomatoes', 'ham'])

注意我是如何使用在margheritaprosciutto工廠方法中使用cls參數(shù),而不是直接調(diào)用Pizza構(gòu)造函數(shù)。

這是一個技巧,您可以用來遵循“不要重蹈覆轍”(DRY)的原則。如果我們決定在某個時候重命名該類,則不必記住在所有類方法工廠函數(shù)中都更新構(gòu)造函數(shù)名稱。

現(xiàn)在,我們可以用這些工廠方法做什么?讓我們試用一下:

>>> Pizza.margherita()
Pizza(['mozzarella', 'tomatoes'])

>>> Pizza.prosciutto()
Pizza(['mozzarella', 'tomatoes', 'ham'])

正如您所看到的,我們可以使用工廠函數(shù)創(chuàng)建新的Pizza對象,這些對象按照我們想要的方式配置。它們都在內(nèi)部使用相同的__init__構(gòu)造函數(shù),只是為記住所有不同的成分提供了一個快捷方式。

另一種看待類方法用法的方法是,它們允許您為類定義替代構(gòu)造函數(shù)。

Python只允許每個類使用一個__init__方法。使用類方法可以根據(jù)需要添加任意多的可選構(gòu)造函數(shù)。這可以使類的接口自文檔化(在一定程度上),并簡化它們的使用。

什么時候使用靜態(tài)方法

這里要想出一個好的例子有點困難。但是告訴你,我會繼續(xù)把比薩餅拉得越來越薄…(好吃!)這是我想到的:

import math

class Pizza:
    def __init__(self, radius, ingredients):
        self.radius = radius
        self.ingredients = ingredients

    def __repr__(self):
        return (f'Pizza({self.radius!r}, '
                f'{self.ingredients!r})')

    def area(self):
        return self.circle_area(self.radius)

    @staticmethod
    def circle_area(r):
        return r ** 2 * math.pi

現(xiàn)在我改變了什么?首先,我修改了構(gòu)造函數(shù)和__repr__以接受一個額外的radius參數(shù)。

我還添加了一個area()實例方法來計算并返回比薩餅的面積(這也是@property的一個很好的候選者-但是,這只是一個玩具示例)。

而不是計算面積直接在area()中,使用眾所周知的圓面積公式,我將其分解為一個單獨的circle_area()靜態(tài)方法。

讓我們試試!

>>> p = Pizza(4, ['mozzarella', 'tomatoes'])
>>> p
Pizza(4, ['mozzarella', 'tomatoes'])
>>> p.area()
50.26548245743669
>>> Pizza.circle_area(4)
50.26548245743669

當(dāng)然,這是一個有點簡單的例子,但它可以幫助解釋靜態(tài)方法提供的一些好處。

正如我們所了解的,靜態(tài)方法不能訪問類或?qū)嵗隣顟B(tài),因為它們不接受clsself參數(shù)。這是一個很大的限制-但它也是一個很好的信號,表明一個特定的方法獨立于它周圍的所有其他方法。

在上面的示例中,很明顯circle_area()不能以任何方式修改類或類實例。(當(dāng)然,你可以用一個全局變量來解決這個問題,但這不是重點。)

現(xiàn)在,為什么這很有用?

將一個方法標(biāo)記為靜態(tài)方法不僅僅意味著一個方法不會修改類或?qū)嵗隣顟B(tài)-Python運行時也會強制執(zhí)行此限制。

類似的技術(shù)允許您清楚地交流類體系結(jié)構(gòu)的各個部分,以便新的開發(fā)工作自然地在這些體系結(jié)構(gòu)中進(jìn)行設(shè)置邊界。當(dāng)然,挑戰(zhàn)這些限制是很容易的。但在實踐中,它們通常有助于避免意外的修改違背原始設(shè)計。

換言之,使用靜態(tài)方法和類方法是傳達(dá)開發(fā)人員意圖的方法,同時強制執(zhí)行該意圖,以避免大多數(shù)會破壞設(shè)計的思維失誤和錯誤。

謹(jǐn)慎地應(yīng)用用這種方式編寫某些方法可以提供維護(hù)好處,減少其他開發(fā)人員錯誤使用您的類的可能性,這是有道理的。

在編寫測試代碼時,靜態(tài)方法也有好處。

因為circle_area()方法完全獨立于類的其他部分,所以它非常重要更容易測試。

在單元測試中測試方法之前,我們不必?fù)?dān)心設(shè)置一個完整的類實例。我們可以像測試常規(guī)函數(shù)一樣發(fā)射出去。同樣,這使以后的維護(hù)更容易。

關(guān)鍵要點

  • 實例方法需要一個類實例,并且可以通過訪問該實例self。

  • 類方法不需要類實例。他們無法訪問實例(self),但可以通過訪問類本身cls。

  • 靜態(tài)方法無權(quán)訪問cls或self。它們像常規(guī)函數(shù)一樣工作,但屬于類的名稱空間。

  • 靜態(tài)方法和類方法進(jìn)行通信,并(在一定程度上)強制開發(fā)人員進(jìn)行有關(guān)類設(shè)計的意圖。這可以帶來維護(hù)優(yōu)勢。