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

歡迎訪問 生活随笔!

生活随笔

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

python

python __reduce__魔法方法_非常全的通俗易懂 Python 魔法方法指南(下)

發(fā)布時間:2024/9/27 python 24 豆豆
生活随笔 收集整理的這篇文章主要介紹了 python __reduce__魔法方法_非常全的通俗易懂 Python 魔法方法指南(下) 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

點擊上方“咸魚學(xué)Python”,選擇“加為星標(biāo)”

第一時間關(guān)注Python技術(shù)干貨!

作者:Rafe Kettler

翻譯:hit9

來源:https://pyzh.readthedocs.io/en/latest/python-magic-methods-guide.html

06. 反射

你可以通過定義魔法方法來控制用于反射的內(nèi)建函數(shù) isinstance 和 issubclass 的行為。下面是對應(yīng)的魔法方法:__instancecheck__(self, instance)

檢查一個實例是否是你定義的類的一個實例(例如 isinstance(instance, class) )。

__subclasscheck__(self, subclass)

檢查一個類是否是你定義的類的子類(例如 issubclass(subclass, class) )。

這幾個魔法方法的適用范圍看起來有些窄,事實也正是如此。我不會在反射魔法方法上花費(fèi)太多時間,因為相比其他魔法方法它們顯得不是很重要。但是它們展示了在Python中進(jìn)行面向?qū)ο缶幊?#xff08;或者總體上使用Python進(jìn)行編程)時很重要的一點:不管做什么事情,都會有一個簡單方法,不管它常用不常用。這些魔法方法可能看起來沒那么有用,但是當(dāng)你真正需要用到它們的時候,你會感到很幸運(yùn),因為它們還在那兒(也因為你閱讀了這本指南!)

07. 抽象基類

請參考 http://docs.python.org/2/library/abc.html

08. 可調(diào)用的對象

你可能已經(jīng)知道了,在Python中,函數(shù)是一等的對象。這意味著它們可以像其他任何對象一樣被傳遞到函數(shù)和方法中,這是一個十分強(qiáng)大的特性。

Python中一個特殊的魔法方法允許你自己類的對象表現(xiàn)得像是函數(shù),然后你就可以“調(diào)用”它們,把它們傳遞到使用函數(shù)做參數(shù)的函數(shù)中,等等等等。這是另一個強(qiáng)大而且方便的特性,讓使用Python編程變得更加幸福。__call__ (self, [args…])

允許類的一個實例像函數(shù)那樣被調(diào)用。本質(zhì)上這代表了 x() 和 x.__call__() 是相同的。注意 __call__ 可以有多個參數(shù),這代表你可以像定義其他任何函數(shù)一樣,定義 __call__ ,喜歡用多少參數(shù)就用多少。

__call__ 在某些需要經(jīng)常改變狀態(tài)的類的實例中顯得特別有用?!罢{(diào)用”這個實例來改變它的狀態(tài),是一種更加符合直覺,也更加優(yōu)雅的方法。一個表示平面上實體的類是一個不錯的例子:class Entity:

'''表示一個實體的類,調(diào)用它的實例

可以更新實體的位置'''

def __init__(self, size, x, y):

self.x, self.y = x, y

self.size = size

def __call__(self, x, y):

'''改變實體的位置'''

self.x, self.y = x, y

09. 上下文管理器

在Python 2.5中引入了一個全新的關(guān)鍵詞,隨之而來的是一種新的代碼復(fù)用方法—— with 聲明。上下文管理的概念在Python中并不是全新引入的(之前它作為標(biāo)準(zhǔn)庫的一部分實現(xiàn)),直到PEP 343被接受,它才成為一種一級的語言結(jié)構(gòu)??赡苣阋呀?jīng)見過這種寫法了:with open('foo.txt') as bar:

# 使用bar進(jìn)行某些操作

當(dāng)對象使用 with 聲明創(chuàng)建時,上下文管理器允許類做一些設(shè)置和清理工作。上下文管理器的行為由下面兩個魔法方法所定義:__enter__(self)

定義使用 with 聲明創(chuàng)建的語句塊最開始上下文管理器應(yīng)該做些什么。注意 __enter__ 的返回值會賦給 with 聲明的目標(biāo),也就是 as 之后的東西。

__exit__(self, exception_type, exception_value, traceback)

定義當(dāng) with 聲明語句塊執(zhí)行完畢(或終止)時上下文管理器的行為。它可以用來處理異常,進(jìn)行清理,或者做其他應(yīng)該在語句塊結(jié)束之后立刻執(zhí)行的工作。如果語句塊順利執(zhí)行, exception_type , exception_value 和 traceback 會是 None 。否則,你可以選擇處理這個異?;蛘咦層脩魜硖幚?。如果你想處理異常,確保 __exit__ 在完成工作之后返回 True 。如果你不想處理異常,那就讓它發(fā)生吧。

對一些具有良好定義的且通用的設(shè)置和清理行為的類,__enter__ 和 __exit__會顯得特別有用。你也可以使用這幾個方法來創(chuàng)建通用的上下文管理器,用來包裝其他對象。下面是一個例子:class Closer:

'''一個上下文管理器,可以在with語句中

使用close()自動關(guān)閉對象'''

def __init__(self, obj):

self.obj = obj

def __enter__(self, obj):

return self.obj # 綁定到目標(biāo)

def __exit__(self, exception_type, exception_value, traceback):

try:

self.obj.close()

except AttributeError: # obj不是可關(guān)閉的

print 'Not closable.'

return True # 成功地處理了異常

這是一個 Closer 在實際使用中的例子,使用一個FTP連接來演示(一個可關(guān)閉的socket):>>> from magicmethods import Closer

>>> from ftplib import FTP

>>> with Closer(FTP('ftp.somesite.com')) as conn:

... conn.dir()

...

# 為了簡單,省略了某些輸出

>>> conn.dir()

# 很長的 AttributeError 信息,不能使用一個已關(guān)閉的連接

>>> with Closer(int()) as i:

... i +=

...

Not closable.

>>> i

看到我們的包裝器是如何同時優(yōu)雅地處理正確和不正確的調(diào)用了嗎?這就是上下文管理器和魔法方法的力量。Python標(biāo)準(zhǔn)庫包含一個 contextlib 模塊,里面有一個上下文管理器 contextlib.closing() 基本上和我們的包裝器完成的是同樣的事情(但是沒有包含任何當(dāng)對象沒有close()方法時的處理)。

10. 創(chuàng)建描述符對象

描述符是一個類,當(dāng)使用取值,賦值和刪除 時它可以改變其他對象。描述符不是用來單獨(dú)使用的,它們需要被一個擁有者類所包含。描述符可以用來創(chuàng)建面向?qū)ο髷?shù)據(jù)庫,以及創(chuàng)建某些屬性之間互相依賴的類。描述符在表現(xiàn)具有不同單位的屬性,或者需要計算的屬性時顯得特別有用(例如表現(xiàn)一個坐標(biāo)系中的點的類,其中的距離原點的距離這種屬性)。

要想成為一個描述符,一個類必須具有實現(xiàn) __get__ , __set__ 和 __delete__ 三個方法中至少一個。

讓我們一起來看一看這些魔法方法:__get__(self, instance, owner)

定義當(dāng)試圖取出描述符的值時的行為。instance 是擁有者類的實例, owner 是擁有者類本身。

__set__(self, instance, owner)

定義當(dāng)描述符的值改變時的行為。instance 是擁有者類的實例, value 是要賦給描述符的值。

__delete__(self, instance, owner)

定義當(dāng)描述符的值被刪除時的行為。instance 是擁有者類的實例

現(xiàn)在,來看一個描述符的有效應(yīng)用:單位轉(zhuǎn)換:class Meter(object):

'''米的描述符。'''

def __init__(self, value=0.0):

self.value = float(value)

def __get__(self, instance, owner):

return self.value

def __set__(self, instance, owner):

self.value = float(value)

class Foot(object):

'''英尺的描述符。'''

def __get__(self, instance, owner):

return instance.meter * 3.2808

def __set__(self, instance, value):

instance.meter = float(value) / 3.2808

class Distance(object):

'''用于描述距離的類,包含英尺和米兩個描述符。'''

meter = Meter()

foot = Foot()

11. 拷貝

有些時候,特別是處理可變對象時,你可能想拷貝一個對象,改變這個對象而不影響原有的對象。這時就需要用到Python的 copy 模塊了。然而(幸運(yùn)的是),Python模塊并不具有感知能力, 因此我們不用擔(dān)心某天基于Linux的機(jī)器人崛起。但是我們的確需要告訴Python如何有效率地拷貝對象。__copy__(self)

定義對類的實例使用 copy.copy() 時的行為。copy.copy() 返回一個對象的淺拷貝,這意味著拷貝出的實例是全新的,然而里面的數(shù)據(jù)全都是引用的。也就是說,對象本身是拷貝的,但是它的數(shù)據(jù)還是引用的(所以淺拷貝中的數(shù)據(jù)更改會影響原對象)。

__deepcopy__(self, memodict=)

定義對類的實例使用 copy.deepcopy() 時的行為。copy.deepcopy() 返回一個對象的深拷貝,這個對象和它的數(shù)據(jù)全都被拷貝了一份。memodict 是一個先前拷貝對象的緩存,它優(yōu)化了拷貝過程,而且可以防止拷貝遞歸數(shù)據(jù)結(jié)構(gòu)時產(chǎn)生無限遞歸。當(dāng)你想深拷貝一個單獨(dú)的屬性時,在那個屬性上調(diào)用 copy.deepcopy() ,使用 memodict 作為第一個參數(shù)。

這些魔法方法有什么用武之地呢?像往常一樣,當(dāng)你需要比默認(rèn)行為更加精確的控制時。例如,如果你想拷貝一個對象,其中存儲了一個字典作為緩存(可能會很大),拷貝緩存可能是沒有意義的。如果這個緩存可以在內(nèi)存中被不同實例共享,那么它就應(yīng)該被共享。

12. Pickling

如果你和其他的Python愛好者共事過,很可能你已經(jīng)聽說過Pickling了。Pickling是Python數(shù)據(jù)結(jié)構(gòu)的序列化過程,當(dāng)你想存儲一個對象稍后再取出讀取時,Pickling會顯得十分有用。然而它同樣也是擔(dān)憂和混淆的主要來源。

Pickling是如此的重要,以至于它不僅僅有自己的模塊( pickle ),還有自己的協(xié)議和魔法方法。首先,我們先來簡要的介紹一下如何pickle已存在的對象類型(如果你已經(jīng)知道了,大可跳過這部分內(nèi)容)。

12.1 小試牛刀

我們一起來pickle吧。假設(shè)你有一個字典,你想存儲它,稍后再取出來。你可以把它的內(nèi)容寫入一個文件,小心翼翼地確保使用了正確地格式,要把它讀取出來,你可以使用 exec() 或處理文件輸入。但是這種方法并不可靠:如果你使用純文本來存儲重要數(shù)據(jù),數(shù)據(jù)很容易以多種方式被破壞或者修改,導(dǎo)致你的程序崩潰,更糟糕的情況下,還可能在你的計算機(jī)上運(yùn)行惡意代碼。因此,我們要pickle它:import pickle

data = {'foo': [,,],

'bar': ('Hello', 'world!'),

'baz': True}

jar = open('data.pkl', 'wb')

pickle.dump(data, jar) # 將pickle后的數(shù)據(jù)寫入jar文件

jar.close()

過了幾個小時,我們想把它取出來,我們只需要反pickle它:import pickle

pkl_file = open('data.pkl', 'rb') # 與pickle后的數(shù)據(jù)連接

data = pickle.load(pkl_file) # 把它加載進(jìn)一個變量

print data

pkl_file.close()

將會發(fā)生什么?正如你期待的,它就是我們之前的 data 。

現(xiàn)在,還需要謹(jǐn)慎地說一句:pickle并不完美。Pickle文件很容易因為事故或被故意的破壞掉。Pickling或許比純文本文件安全一些,但是依然有可能被用來運(yùn)行惡意代碼。而且它還不支持跨Python版本,所以不要指望分發(fā)pickle對象之后所有人都能正確地讀取。然而不管怎么樣,它依然是一個強(qiáng)有力的工具,可以用于緩存和其他類型的持久化工作。

12.2 Pickle你的對象

Pickle不僅僅可以用于內(nèi)建類型,任何遵守pickle協(xié)議的類都可以被pickle。Pickle協(xié)議有四個可選方法,可以讓類自定義它們的行為(這和C語言擴(kuò)展略有不同,那不在我們的討論范圍之內(nèi))。__getinitargs__(self)

如果你想讓你的類在反pickle時調(diào)用 __init__ ,你可以定義 __getinitargs__(self) ,它會返回一個參數(shù)元組,這個元組會傳遞給 __init__ 。注意,這個方法只能用于舊式類。

__getnewargs__(self)

對新式類來說,你可以通過這個方法改變類在反pickle時傳遞給 __new__ 的參數(shù)。這個方法應(yīng)該返回一個參數(shù)元組。

__getstate__(self)

你可以自定義對象被pickle時被存儲的狀態(tài),而不使用對象的 __dict__ 屬性。這個狀態(tài)在對象被反pickle時會被 __setstate__ 使用。

__setstate__(self)

當(dāng)一個對象被反pickle時,如果定義了 __setstate__ ,對象的狀態(tài)會傳遞給這個魔法方法,而不是直接應(yīng)用到對象的 __dict__ 屬性。這個魔法方法和 __getstate__ 相互依存:當(dāng)這兩個方法都被定義時,你可以在Pickle時使用任何方法保存對象的任何狀態(tài)。

__reduce__(self)

當(dāng)定義擴(kuò)展類型時(也就是使用Python的C語言API實現(xiàn)的類型),如果你想pickle它們,你必須告訴Python如何pickle它們。reduce 被定義之后,當(dāng)對象被Pickle時就會被調(diào)用。它要么返回一個代表全局名稱的字符串,Pyhton會查找它并pickle,要么返回一個元組。這個元組包含2到5個元素,其中包括:一個可調(diào)用的對象,用于重建對象時調(diào)用;一個參數(shù)元素,供那個可調(diào)用對象使用;被傳遞給 __setstate__ 的狀態(tài)(可選);一個產(chǎn)生被pickle的列表元素的迭代器(可選);一個產(chǎn)生被pickle的字典元素的迭代器(可選);

__reduce_ex__(self)

__reduce_ex__ 的存在是為了兼容性。如果它被定義,在pickle時 __reduce_ex__ 會代替 __reduce__ 被調(diào)用。__reduce__ 也可以被定義,用于不支持 __reduce_ex__ 的舊版pickle的API調(diào)用。

12.3 一個例子

我們的例子是 Slate ,它會記住它的值曾經(jīng)是什么,以及那些值是什么時候賦給它的。然而 每次被pickle時它都會變成空白,因為當(dāng)前的值不會被存儲:import time

class Slate:

'''存儲一個字符串和一個變更日志的類

每次被pickle都會忘記它當(dāng)前的值'''

def __init__(self, value):

self.value = value

self.last_change = time.asctime()

self.history = {}

def change(self, new_value):

# 改變當(dāng)前值,將上一個值記錄到歷史

self.history[self.last_change] = self.value

self.value = new_value)

self.last_change = time.asctime()

def print_change(self):

print 'Changelog for Slate object:'

for k,v in self.history.items():

print '%s\t %s' % (k,v)

def __getstate__(self):

# 故意不返回self.value或self.last_change

# 我們想在反pickle時得到一個空白的slate

return self.history

def __setstate__(self):

# 使self.history = slate,last_change

# 和value為未定義

self.history = state

self.value, self.last_change = None, None

總結(jié)在最后

這本指南的目標(biāo)是使所有閱讀它的人都能有所收獲,無論他們有沒有使用Python或者進(jìn)行面向?qū)ο缶幊痰慕?jīng)驗。如果你剛剛開始學(xué)習(xí)Python,你會得到寶貴的基礎(chǔ)知識,了解如何寫出具有豐富特性的,優(yōu)雅而且易用的類。如果你是中級的Python程序員,你或許能掌握一些新的概念和技巧,以及一些可以減少代碼行數(shù)的好辦法。如果你是專家級別的Python愛好者,你又重新復(fù)習(xí)了一遍某些可能已經(jīng)忘掉的知識,也可能順便了解了一些新技巧。無論你的水平怎樣,我希望這趟遨游Python特殊方法的旅行,真的對你產(chǎn)生了魔法般的效果(實在忍不住不說最后這個雙關(guān))。

Love & Share

[ 完 ]

總結(jié)

以上是生活随笔為你收集整理的python __reduce__魔法方法_非常全的通俗易懂 Python 魔法方法指南(下)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

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