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

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 人文社科 > 生活经验 >内容正文

生活经验

Python __call__详解

發(fā)布時間:2023/11/28 生活经验 22 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Python __call__详解 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

20210507

相當(dāng)于函數(shù)的默認(rèn)調(diào)用()?

可以調(diào)用的對象

關(guān)于 __call__ 方法,不得不先提到一個概念,就是可調(diào)用對象(callable),我們平時自定義的函數(shù)、內(nèi)置函數(shù)和類都屬于可調(diào)用對象,但凡是可以把一對括號()應(yīng)用到某個對象身上都可稱之為可調(diào)用對象,判斷對象是否為可調(diào)用對象可以用函數(shù) callable

如果在類中實現(xiàn)了 __call__ 方法,那么實例對象也將成為一個可調(diào)用對象,

你也許已經(jīng)知道,在Python中,方法也是一種高等的對象。這意味著他們也可以被傳遞到方法中就像其他對象一樣。這是一個非常驚人的特性。 在Python中,一個特殊的魔術(shù)方法可以讓類的實例的行為表現(xiàn)的像函數(shù)一樣,你可以調(diào)用他們,將一個函數(shù)當(dāng)做一個參數(shù)傳到另外一個函數(shù)中等等。這是一個非常強大的特性讓Python編程更加舒適甜美。 __call__(self, [args...])

允許一個類的實例像函數(shù)一樣被調(diào)用。實質(zhì)上說,這意味著 x() 與 x.__call__() 是相同的。注意 __call__ 參數(shù)可變。這意味著你可以定義 __call__ 為其他你想要的函數(shù),無論有多少個參數(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, yself.size = sizedef __call__(self, x, y):'''改變實體的位置'''self.x, self.y = x, ye = Entity(1, 2, 3) // 創(chuàng)建實例
e(4, 5) //實例可以象函數(shù)那樣執(zhí)行,并傳入x y值,修改對象的x y 

實例 --- Flask Wtform

在wtform的validators就使用了這個特性
wtform 定義字段的時候可以為每個字段添加校驗器, 而每個校驗器的特點都是接受兩個參數(shù),form, field。
所以我們可以自己自定義校驗器,校驗器是可調(diào)用對象即可,即函數(shù),對象方法,都可以的。
比如function url_validate(form, field)

使用函數(shù)定制

下面提供一個my_length_check()函數(shù),用于驗證name長達是否長于50個字符。
這個函數(shù)按照規(guī)定接受兩個參數(shù),form, field,然后就可以根據(jù)兩個參數(shù)進行判斷。
這樣做是可以的,但是問題是:如果我想自定義錯誤信息怎么辦?而且我想限制的數(shù)量也能控制,不是寫死, 還有如果有最小值,和最大值呢? 這里只能固定接受兩個參數(shù),不能再傳入自定參數(shù),沒得更多的自定義了,這樣就會限制住了。


def my_length_check(form, field):if len(field.data) > 50:raise ValidationError('Field must be less than 50 characters')class MyForm(Form):name = StringField('Name', [InputRequired(), my_length_check])

使用閉包和工廠模式

為了解決上面能傳入更多自定義參數(shù), 更靈活定制校驗,我們進行改造。
使用工廠模式,一個能生產(chǎn)校驗器的工廠,而這個工廠能接受其他更靈活的參數(shù),看例子:


def length(min=-1, max=-1, message=None):if not message:message = 'Must be between %d and %d characters long.' % (min, max)def _length(form, field):l = field.data and len(field.data) or 0if l < min or max != -1 and l > max:raise ValidationError(message)return _lengthclass MyForm(Form):name = StringField('Name', [InputRequired(), length(max=50)])

這里涉及兩個概念:

  • 工廠模式
    這個例子的length()是一個工廠,他的工作就是執(zhí)行的時候,生產(chǎn)一個validator校驗器,每次調(diào)用,傳入不同參數(shù),就會生產(chǎn)不同的校驗器,所以叫做工廠模式。

  • 閉包
    同時它也是個閉包, 為什么是個閉包?正常情況下執(zhí)行完length() min, max, message參數(shù)就會被回收,不在存在,但是為什么_length()能繼續(xù)讀取min, max, message變量,就是閉包的威力。閉包使得局部變量在函數(shù)外被訪問成為可能。
    這里的 length() 就是一個閉包,閉包本質(zhì)上是一個函數(shù),它有兩部分組成,_length 函數(shù)和變量 min, max, message。閉包使得這些變量的值始終保存在內(nèi)存中。
    參考:一步一步教你認(rèn)識Python閉包

這個length(min, max, message) 函數(shù),調(diào)用的時候,傳入了更多的參數(shù)來靈活決定校驗器,因為他里面就是返回一個_length的校驗器,這個校驗器還是遵循規(guī)則,只接受form, field 參數(shù), 但是在length() 這個外層卻能接受更多參數(shù),而這些參數(shù)也能被內(nèi)層的_length()所使用。


class MyForm(Form):name = StringField('Name', [InputRequired(), length(max=50)])

這里的length(max=5)就是返回了個_length函數(shù),wtform調(diào)用校驗器的時候?qū)嶋H上就是這樣調(diào)用的:

length(max=50)(form, field)

使用類方法__call__實現(xiàn)


class Length(object):def __init__(self, min=-1, max=-1, message=None):self.min = minself.max = maxif not message:message = u'Field must be between %i and %i characters long.' % (min, max)self.message = messagedef __call__(self, form, field):l = field.data and len(field.data) or 0if l < self.min or self.max != -1 and l > self.max:raise ValidationError(self.message)class MyForm(Form):name = StringField('Name', [InputRequired(), Length(max=50)])

這次我們使用類來實現(xiàn),首先實例化這個校驗器Length(max=50), 這時返回的是對象實例,然后我們定義了方法__call__方法,說明實例對象是可以調(diào)用的, 最后的結(jié)果就是Length(max=50)(form, field)。
這樣實現(xiàn)方法是也是很妙的。
說到這里,類實現(xiàn)方法跟閉包很像,都是把變量封裝起來,讓真正的校驗器能讀取到參數(shù)。

閉包避免了使用全局變量,此外,閉包允許將函數(shù)與其所操作的某些數(shù)據(jù)(環(huán)境)關(guān)連起來。這一點與面向?qū)ο缶幊淌欠浅n愃频?#xff0c;在面對象編程中,對象允許我們將某些數(shù)據(jù)(對象的屬性)與一個或者多個方法相關(guān)聯(lián)。

__call__其他作用

實例對象也可以像函數(shù)一樣作為可調(diào)用對象來用,那么,這個特點在什么場景用得上呢?這個要結(jié)合類的特性來說,類可以記錄數(shù)據(jù)(屬性),而函數(shù)不行(閉包某種意義上也可行),利用這種特性可以實現(xiàn)基于類的裝飾器,在類里面記錄狀態(tài),比如,下面這個例子用于記錄函數(shù)被調(diào)用的次數(shù):


class Counter:def __init__(self, func):self.func = funcself.count = 0def __call__(self, *args, **kwargs):self.count += 1return self.func(*args, **kwargs)@Counter
def foo():passfor i in range(10):foo()print(foo.count)  # 10

首先這里的@Counter是裝飾器,執(zhí)行起來順序是 foo = Counter(foo), 實例化,把foo函數(shù)傳到類Counter里面,并存到對象屬性里面,然后返回foo = Counter實例。 這時foo已經(jīng)是Counter實例,而不是本身foo函數(shù)。
當(dāng)執(zhí)行foo()的時候,其實已經(jīng)變成了,執(zhí)行__call__函數(shù),而這個函數(shù)里面是執(zhí)行了本身的self.func 即foo的實際邏輯, 而且加上了計算調(diào)用次數(shù)。這樣就記錄狀態(tài)了。
太厲害了,這樣的實現(xiàn)方式。

https://wtforms.readthedocs.io/en/stable/validators.html#custom-validators
https://stackoverflow.com/questions/9663562/what-is-the-difference-between-init-and-call-in-python
一步一步教你認(rèn)識Python閉包
簡述 init、new、call 方法



作者:大富帥
鏈接:https://www.jianshu.com/p/e1d95c4e1697
來源:簡書
簡書著作權(quán)歸作者所有,任何形式的轉(zhuǎn)載都請聯(lián)系作者獲得授權(quán)并注明出處。

總結(jié)

以上是生活随笔為你收集整理的Python __call__详解的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

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