日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程语言 > python >内容正文

python

python中的静态方法如何调用_关于Python中如何使用静态、类、抽象方法的权威指南(译)...

發(fā)布時(shí)間:2025/3/19 python 30 豆豆
生活随笔 收集整理的這篇文章主要介紹了 python中的静态方法如何调用_关于Python中如何使用静态、类、抽象方法的权威指南(译)... 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

對(duì)于Python中靜態(tài)、類(lèi)、抽象方法的使用,我是一直很迷糊的。最近看到一篇技術(shù)文章對(duì)這方面解釋的很好,在此翻譯一下,加深印象,也為有需要的同學(xué)提供一個(gè)方便。

Python中方法是如何工作的:

方法即函數(shù),作為一個(gè)類(lèi)的屬性存儲(chǔ)。你能像如下申明和訪問(wèn)一個(gè)函數(shù):

>>> class Pizza(object):

... def __init__(self,size):

... self.size = size

... def get_size(self):

... return self.size

...

>>> Pizza.get_size

Python在這里告訴我們,Pizza類(lèi)的get_size屬性的訪問(wèn)時(shí)沒(méi)有綁定。這是什么意思呢?我們馬上就會(huì)知道只要我們繼續(xù)調(diào)用它一下:

>>> Pizza.get_size()

Traceback (most recent call last):

File "", line 1, in

TypeError: unbound method get_size() must be called with Pizza instance as first

argument (got nothing instead)

我們不能調(diào)用它,是因?yàn)樗鼪](méi)有綁定到任何Pizza的實(shí)例。方法需要一個(gè)實(shí)例作為它的第一個(gè)參數(shù)(在Python 2中它必須是該類(lèi)的一個(gè)實(shí)例,在Python 3中它可以是任何實(shí)例),讓我們?cè)囈幌?#xff1a;

>>> Pizza.get_size(Pizza(42))

42

它工作了!我們調(diào)用這個(gè)方法時(shí),把一個(gè)實(shí)例作為它的第一個(gè)參數(shù),這樣就一切正常了。但是你會(huì)認(rèn)同我的觀點(diǎn):這并不是一個(gè)方便的方式來(lái)調(diào)用方法。我們每次想要調(diào)用方法的時(shí)候都要引用類(lèi)。如果我們并不知道哪個(gè)類(lèi)使我們的對(duì)象,在很長(zhǎng)時(shí)間內(nèi)這中方式是行不通的。

因此,Python為我們做了綁定Pizza類(lèi)的所有方法到該類(lèi)的任意實(shí)例上。這就意味著Pizza類(lèi)的實(shí)例的get_size屬性是一個(gè)綁定方法:該方法的第一個(gè)參數(shù)就是實(shí)例本身:

>>> Pizza(42).get_size

>

>>> Pizza(42).get_size()

42

意料之中,我們不再需要為get_size提供任何參數(shù)了,因?yàn)樗墙壎ǖ?#xff0c;它的self參數(shù)自動(dòng)設(shè)置為我們的Pizza實(shí)例。這里有一個(gè)更好的證明:

>>> m = Pizza(42).get_size

>>> m()

42

事實(shí)上,你甚至不必維持一個(gè)到你Pizza對(duì)象的引用。它的方法被綁定到對(duì)象,所以該方法對(duì)自己而言已經(jīng)足夠了。

但是,如果你想知道這個(gè)綁定方法綁定的到底是哪個(gè)對(duì)象?這里有一個(gè)小竅門(mén):

>>> m = Pizza(42).get_size

>>> m.__self__

>>>

>>> m == m.__self__.get_size

True

顯然,我們依然有一個(gè)到對(duì)象的引用,如果有需要可以找回來(lái)。

在Python 3中,附加到類(lèi)的方法不再視為綁定方法了,僅作為簡(jiǎn)單函數(shù)。如果有需要他們綁定到一個(gè)對(duì)象。原理依然保持不變,但是模型簡(jiǎn)化了。

>>> class Pizza(object):

... def __init__(self,size):

... self.size = size

... def get_size(self):

... return self.size

...

>>> Pizza.get_size

靜態(tài)方法:

靜態(tài)方法是方法的一種特殊情況。有時(shí)候,你需要編寫(xiě)屬于某個(gè)類(lèi)的代碼,但是從不使用對(duì)象本身。例如:

>>> class Pizza(object):

... @staticmethod

... def mix_ingredients(x,y):

... return x+y

... def cook(self):

... return self.mix_ingredient(self.cheese,self.vegetables)

...

在這種情況,將mix_ingredients作為非靜態(tài)函數(shù)也能工作,但是必須提供一個(gè)self參數(shù)(不會(huì)被用到)。在這里,裝飾器@staticmethod為我們提供了幾件事情:

Python沒(méi)有實(shí)例化我們實(shí)例化的Pizza對(duì)象的綁定函數(shù)。綁定函數(shù)也是對(duì)象,創(chuàng)造它們是有開(kāi)銷(xiāo)的。使用靜態(tài)函數(shù)可以避免這些:

>>> Pizza().cook is Pizza().cook

False

>>> Pizza().mix_ingredients is Pizza.mix_ingredients

True

>>> Pizza().mix_ingredients is Pizza().mix_ingredients

True

簡(jiǎn)化了代碼的可讀性:看到@staticmethod,我們知道,該方法不依賴對(duì)象本身的狀態(tài);

它允許我們?cè)谧宇?lèi)中重載mix_ingredients方法。如果使用的一個(gè)定義在我們模塊最頂層的mix_ingredients函數(shù),繼承自Pizza的類(lèi)在沒(méi)有重載cook本身的情況下,不能改變我們用于混合pizza的成分。

類(lèi)方法:

說(shuō)了這么多,那么什么是類(lèi)方法?類(lèi)方法是不綁定到對(duì)象但是綁定到類(lèi)的方法。(注意我下面標(biāo)紅的部分,與原文有出入,我在Python 2.7.9和Python 3.4.3下運(yùn)行得到的都是False)

>>> class Pizza(object):

... radius = 42

... @classmethod

... def get_radius(cls):

... return cls.radius

...

>>> Pizza.get_radius

>

>>> Pizza().get_radius

>

>>> Pizza.get_radius is Pizza().get_radius

False

>>> Pizza.get_radius()

42

不管你使用什么方式來(lái)訪問(wèn)這個(gè)方法,它總是綁定于它依附的類(lèi),而且它的第一個(gè)參數(shù)是類(lèi)本身(記住類(lèi)也是對(duì)象)。

那么,什么時(shí)候時(shí)候這種類(lèi)型的方法呢?class方法常用于一下兩種類(lèi)型的方法中:

工廠方法,即用于創(chuàng)建一個(gè)類(lèi)的實(shí)例用于某種預(yù)處理。如果我們使用@staticmethod代替,我們將不得不把Pizza類(lèi)的名字硬編碼到我們的函數(shù)中。這樣使得繼承自Pizza的類(lèi)都無(wú)法使用我們的工廠供自己使用。

>>> class Pizza(object):

... def __init__(self, ingredients):

... self.ingredients = ingredients

...

... @classmethod

... def from_fridge(cls, fridge):

... return cls(fridge.get_cheese() + fridge.get_vegetables())

...

靜態(tài)方法調(diào)用靜態(tài)方法:如果你把靜態(tài)方法拆分到幾個(gè)靜態(tài)方法中,你不應(yīng)該使用硬編碼而使用類(lèi)方法。使用這種方法申明我們的方法,Pizza名字永遠(yuǎn)不會(huì)被引用和繼承并且方法重載會(huì)工作的很好。

>>> class Pizza(object):

... def __init__(self, radius, height):

... self.radius = radius

... self.height = height

...

... @staticmethod

... def compute_area(radius):

... return math.pi * (radius ** 2)

...

... @classmethod

... def compute_volume(cls, height, radius):

... return height * cls.compute_area(radius)

...

... def get_volume(self):

... return self.compute_volume(self.height, self.radius)

...

抽象方法:

抽象方法定義在一個(gè)基類(lèi)中,但是可能沒(méi)有提供任何實(shí)現(xiàn)。在Java中,這種方法被描述為接口。

在Python中最簡(jiǎn)單的寫(xiě)一個(gè)抽象方法的方式如下:

class Pizza(object):

def get_radius(self):

raise NotImplementedError

任何其他繼承自Pizza的類(lèi)應(yīng)該實(shí)現(xiàn)并且重載get_radius方法。否則一個(gè)異常將會(huì)拋出。

這種特殊的實(shí)現(xiàn)抽閑方法的方式有一個(gè)缺點(diǎn)。如果你寫(xiě)一個(gè)繼承自Pizza的類(lèi)并且忘記實(shí)現(xiàn)get_radius了,錯(cuò)誤僅在你打算試用這個(gè)方法的時(shí)候拋出。

>>> Pizza()

>>> Pizza().get_radius()

Traceback (most recent call last):

File "", line 1, in

File "", line 3, in get_radius

NotImplementedError

有一種方法可以早點(diǎn)觸發(fā)這種方式,當(dāng)對(duì)象被實(shí)例化之后,使用Python提供的abc模塊。

>>>

... class BasePizza(object):

... __metaclass__ = abc.ABCMeta

...

... @abc.abstractmethod

... def get_radius(self):

... """Method that should do something."""

...

利用abc和它特殊的類(lèi),只要你嘗試實(shí)例化BasePizza或者任意繼承自它的類(lèi),你都將得到一個(gè)類(lèi)型錯(cuò)誤。

>>> BasePizza()

Traceback (most recent call last):

File "", line 1, in

TypeError: Can't instantiate abstract class BasePizza with abstract methods get_

radius

混合靜態(tài)、類(lèi)和抽象方法:

當(dāng)構(gòu)建類(lèi)和繼承的時(shí)候,你需要混合使用這些方式裝飾的時(shí)候一定會(huì)到來(lái),在這里有關(guān)于它的一些技巧。

請(qǐng)記住聲明方法是抽象的,不會(huì)凍結(jié)該方法的原型。這就意味著,它必須被實(shí)現(xiàn),但是我能用任意參數(shù)列表來(lái)實(shí)現(xiàn)。

import abc

class BasePizza(object):

__metaclass__ = abc.ABCMeta

@abc.abstractmethod

def get_ingredients(self):

"""Returns the ingredient list."""

class Calzone(BasePizza):

def get_ingredients(self, with_egg=False):

egg = Egg() if with_egg else None

return self.ingredients + egg

這是有效的,因?yàn)镃alzone滿足我們?cè)贐asePizza對(duì)象中定義的接口要求。這意味著我們也能作為一個(gè)類(lèi)或者靜態(tài)方法來(lái)實(shí)現(xiàn)它。例如:

import abc

class BasePizza(object):

__metaclass__ = abc.ABCMeta

@abc.abstractmethod

def get_ingredients(self):

"""Returns the ingredient list."""

class DietPizza(BasePizza):

@staticmethod

def get_ingredients():

return None

這也是正確的,符合我們與抽閑BasePizza類(lèi)的合約。事實(shí)上,該get_ingredients方法并不需要知道返回結(jié)果的對(duì)象其實(shí)是一個(gè)實(shí)現(xiàn)細(xì)節(jié),不是一個(gè)讓我們合約履行的標(biāo)準(zhǔn)。

因此,你不能強(qiáng)迫你的抽象方法的實(shí)現(xiàn)是一個(gè)普通的或者類(lèi)或者靜態(tài)方法。從Python 3(這在Python 2是行不通的,參照issue5867)開(kāi)始,它現(xiàn)在可以在@abstractmethod的頂部使用@staticmethod和@classmethod裝飾符。

import abc

class BasePizza(object):

__metaclass__ = abc.ABCMeta

ingredient = ['cheese']

@classmethod

@abc.abstractmethod

def get_ingredients(cls):

"""Returns the ingredient list."""

return cls.ingredients

不要誤讀:如果你覺(jué)得這會(huì)迫使你的子類(lèi)把get_ingredients實(shí)現(xiàn)為一個(gè)類(lèi)的函數(shù)那就錯(cuò)了。這只是意味著你在BasePizza類(lèi)中實(shí)現(xiàn)的get_ingredients是一個(gè)類(lèi)方法。

在一個(gè)抽象方法中的實(shí)現(xiàn)?是的,在Python中,與Java接口相反,你能在抽象方法中編碼并且使用super()調(diào)用它:

import abc

class BasePizza(object):

__metaclass__ = abc.ABCMeta

default_ingredients = ['cheese']

@classmethod

@abc.abstractmethod

def get_ingredients(cls):

"""Returns the ingredient list."""

return cls.default_ingredients

class DietPizza(BasePizza):

def get_ingredients(self):

return ['egg'] + super(DietPizza, self).get_ingredients()

在這種情況下,你建立的每一個(gè)繼承自BasePizza的pizza都不得不重載get_ingredients方法,但可以使用默認(rèn)的機(jī)制,通過(guò)使用super()來(lái)獲取成分列表。

總結(jié)

以上是生活随笔為你收集整理的python中的静态方法如何调用_关于Python中如何使用静态、类、抽象方法的权威指南(译)...的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。