最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

解密Python中的描述符(descriptor)

 更新時(shí)間:2015年06月03日 09:31:12   投稿:junjie  
這篇文章主要介紹了解密Python中的描述符(descriptor),本文詳細(xì)講解了描述符(descriptor)的作用、訪問(wèn)描述符、對(duì)描述符賦值、刪除描述符等內(nèi)容,需要的朋友可以參考下

Python中包含了許多內(nèi)建的語(yǔ)言特性,它們使得代碼簡(jiǎn)潔且易于理解。這些特性包括列表/集合/字典推導(dǎo)式,屬性(property)、以及裝飾器(decorator)。對(duì)于大部分特性來(lái)說(shuō),這些“中級(jí)”的語(yǔ)言特性有著完善的文檔,并且易于學(xué)習(xí)。

但是這里有個(gè)例外,那就是描述符。至少對(duì)于我來(lái)說(shuō),描述符是Python語(yǔ)言核心中困擾我時(shí)間最長(zhǎng)的一個(gè)特性。這里有幾點(diǎn)原因如下:

1.有關(guān)描述符的官方文檔相當(dāng)難懂,而且沒(méi)有包含優(yōu)秀的示例告訴你為什么需要編寫(xiě)描述符(我得為Raymond Hettinger辯護(hù)一下,他寫(xiě)的其他主題的Python文章和視頻對(duì)我的幫助還是非常大的)
2.編寫(xiě)描述符的語(yǔ)法顯得有些怪異
3.自定義描述符可能是Python中用的最少的特性,因此你很難在開(kāi)源項(xiàng)目中找到優(yōu)秀的示例

但是一旦你理解了之后,描述符的確還是有它的應(yīng)用價(jià)值的。這篇文章告訴你描述符可以用來(lái)做什么,以及為什么應(yīng)該引起你的注意。

一句話概括:描述符就是可重用的屬性

在這里我要告訴你:從根本上講,描述符就是可以重復(fù)使用的屬性。也就是說(shuō),描述符可以讓你編寫(xiě)這樣的代碼:

復(fù)制代碼 代碼如下:

f = Foo()
b = f.bar
f.bar = c
del f.bar

而在解釋器執(zhí)行上述代碼時(shí),當(dāng)發(fā)現(xiàn)你試圖訪問(wèn)屬性(b = f.bar)、對(duì)屬性賦值(f.bar = c)或者刪除一個(gè)實(shí)例變量的屬性(del f.bar)時(shí),就會(huì)去調(diào)用自定義的方法。

讓我們先來(lái)解釋一下為什么把對(duì)函數(shù)的調(diào)用偽裝成對(duì)屬性的訪問(wèn)是大有好處的。

property——把函數(shù)調(diào)用偽裝成對(duì)屬性的訪問(wèn)

想象一下你正在編寫(xiě)管理電影信息的代碼。你最后寫(xiě)好的Movie類(lèi)可能看上去是這樣的:

復(fù)制代碼 代碼如下:

class Movie(object):
    def __init__(self, title, rating, runtime, budget, gross):
        self.title = title
        self.rating = rating
        self.runtime = runtime
        self.budget = budget
        self.gross = gross
    def profit(self):
        return self.gross - self.budget

你開(kāi)始在項(xiàng)目的其他地方使用這個(gè)類(lèi),但是之后你意識(shí)到:如果不小心給電影打了負(fù)分怎么辦?你覺(jué)得這是錯(cuò)誤的行為,希望Movie類(lèi)可以阻止這個(gè)錯(cuò)誤。 你首先想到的辦法是將Movie類(lèi)修改為這樣:

復(fù)制代碼 代碼如下:

class Movie(object):
    def __init__(self, title, rating, runtime, budget, gross):
        self.title = title
        self.rating = rating
        self.runtime = runtime
        self.gross = gross
        if budget < 0:
            raise ValueError("Negative value not allowed: %s" % budget)
        self.budget = budget
    def profit(self):
        return self.gross - self.budget

但這行不通。因?yàn)槠渌糠值拇a都是直接通過(guò)Movie.budget來(lái)賦值的——這個(gè)新修改的類(lèi)只會(huì)在init方法中捕獲錯(cuò)誤的數(shù)據(jù),但對(duì)于已經(jīng)存在的類(lèi)實(shí)例就無(wú)能為力了。如果有人試著運(yùn)行m.budget = -100,那么誰(shuí)也沒(méi)法阻止。作為一個(gè)Python程序員同時(shí)也是電影迷,你該怎么辦?

幸運(yùn)的是,Python的property解決了這個(gè)問(wèn)題。如果你從未見(jiàn)過(guò)property的用法,下面是一個(gè)示例:

復(fù)制代碼 代碼如下:

class Movie(object):
    def __init__(self, title, rating, runtime, budget, gross):
        self._budget = None
        self.title = title
        self.rating = rating
        self.runtime = runtime
        self.gross = gross
        self.budget = budget
    @property
    def budget(self):
        return self._budget
    @budget.setter
    def budget(self, value):
        if value < 0:
            raise ValueError("Negative value not allowed: %s" % value)
        self._budget = value
    def profit(self):
        return self.gross - self.budget
m = Movie('Casablanca', 97, 102, 964000, 1300000)
print m.budget       # calls m.budget(), returns result
try:
    m.budget = -100  # calls budget.setter(-100), and raises ValueError
except ValueError:
    print "Woops. Not allowed"
964000
Woops. Not allowed

我們用@property裝飾器指定了一個(gè)getter方法,用@budget.setter裝飾器指定了一個(gè)setter方法。當(dāng)我們這么做時(shí),每當(dāng)有人試著訪問(wèn)budget屬性,Python就會(huì)自動(dòng)調(diào)用相應(yīng)的getter/setter方法。比方說(shuō),當(dāng)遇到m.budget = value這樣的代碼時(shí)就會(huì)自動(dòng)調(diào)用budget.setter。

花點(diǎn)時(shí)間來(lái)欣賞一下Python這么做是多么的優(yōu)雅:如果沒(méi)有property,我們將不得不把所有的實(shí)例屬性隱藏起來(lái),提供大量顯式的類(lèi)似get_budget和set_budget方法。像這樣編寫(xiě)類(lèi)的話,使用起來(lái)就會(huì)不斷的去調(diào)用這些getter/setter方法,這看起來(lái)就像臃腫的Java代碼一樣。更糟的是,如果我們不采用這種編碼風(fēng)格,直接對(duì)實(shí)例屬性進(jìn)行訪問(wèn)。那么稍后就沒(méi)法以清晰的方式增加對(duì)非負(fù)數(shù)的條件檢查——我們不得不重新創(chuàng)建set_budget方法,然后搜索整個(gè)工程中的源代碼,將m.budget = value這樣的代碼替換為m.set_budget(value)。

因此,property讓我們將自定義的代碼同變量的訪問(wèn)/設(shè)定聯(lián)系在了一起,同時(shí)為你的類(lèi)保持一個(gè)簡(jiǎn)單的訪問(wèn)屬性的接口。干得漂亮!

property的不足

對(duì)property來(lái)說(shuō),最大的缺點(diǎn)就是它們不能重復(fù)使用。舉個(gè)例子,假設(shè)你想為rating,runtime和gross這些字段也添加非負(fù)檢查。下面是修改過(guò)的新類(lèi):

復(fù)制代碼 代碼如下:

class Movie(object):
    def __init__(self, title, rating, runtime, budget, gross):
        self._rating = None
        self._runtime = None
        self._budget = None
        self._gross = None
        self.title = title
        self.rating = rating
        self.runtime = runtime
        self.gross = gross
        self.budget = budget
    #nice
    @property
    def budget(self):
        return self._budget
    @budget.setter
    def budget(self, value):
        if value < 0:
            raise ValueError("Negative value not allowed: %s" % value)
        self._budget = value
    #ok   
    @property
    def rating(self):
        return self._rating
    @rating.setter
    def rating(self, value):
        if value < 0:
            raise ValueError("Negative value not allowed: %s" % value)
        self._rating = value
    #uhh...
    @property
    def runtime(self):
        return self._runtime
    @runtime.setter
    def runtime(self, value):
        if value < 0:
            raise ValueError("Negative value not allowed: %s" % value)
        self._runtime = value       
    #is this forever?
    @property
    def gross(self):
        return self._gross
    @gross.setter
    def gross(self, value):
        if value < 0:
            raise ValueError("Negative value not allowed: %s" % value)
        self._gross = value       
    def profit(self):
        return self.gross - self.budget

可以看到代碼增加了不少,但重復(fù)的邏輯也出現(xiàn)了不少。雖然property可以讓類(lèi)從外部看起來(lái)接口整潔漂亮,但是卻做不到內(nèi)部同樣整潔漂亮。

描述符登場(chǎng)(最終的大殺器)

這就是描述符所解決的問(wèn)題。描述符是property的升級(jí)版,允許你為重復(fù)的property邏輯編寫(xiě)單獨(dú)的類(lèi)來(lái)處理。下面的示例展示了描述符是如何工作的(現(xiàn)在還不必?fù)?dān)心NonNegative類(lèi)的實(shí)現(xiàn)):

復(fù)制代碼 代碼如下:

from weakref import WeakKeyDictionary
class NonNegative(object):
    """A descriptor that forbids negative values"""
    def __init__(self, default):
        self.default = default
        self.data = WeakKeyDictionary()
    def __get__(self, instance, owner):
        # we get here when someone calls x.d, and d is a NonNegative instance
        # instance = x
        # owner = type(x)
        return self.data.get(instance, self.default)
    def __set__(self, instance, value):
        # we get here when someone calls x.d = val, and d is a NonNegative instance
        # instance = x
        # value = val
        if value < 0:
            raise ValueError("Negative value not allowed: %s" % value)
        self.data[instance] = value
class Movie(object):
    #always put descriptors at the class-level
    rating = NonNegative(0)
    runtime = NonNegative(0)
    budget = NonNegative(0)
    gross = NonNegative(0)
    def __init__(self, title, rating, runtime, budget, gross):
        self.title = title
        self.rating = rating
        self.runtime = runtime
        self.budget = budget
        self.gross = gross
    def profit(self):
        return self.gross - self.budget
m = Movie('Casablanca', 97, 102, 964000, 1300000)
print m.budget  # calls Movie.budget.__get__(m, Movie)
m.rating = 100  # calls Movie.budget.__set__(m, 100)
try:
    m.rating = -1   # calls Movie.budget.__set__(m, -100)
except ValueError:
    print "Woops, negative value"
964000
Woops, negative value

這里引入了一些新的語(yǔ)法,我們一條條的來(lái)看:
NonNegative是一個(gè)描述符對(duì)象,因?yàn)樗x了__get__,__set__或__delete__方法。
Movie類(lèi)現(xiàn)在看起來(lái)非常清晰。我們?cè)陬?lèi)的層面上創(chuàng)建了4個(gè)描述符,把它們當(dāng)做普通的實(shí)例屬性。顯然,描述符在這里為我們做非負(fù)檢查。

訪問(wèn)描述符

當(dāng)解釋器遇到print m.buget時(shí),它就會(huì)把budget當(dāng)作一個(gè)帶有__get__ 方法的描述符,調(diào)用Movie.budget.__get__方法并將方法的返回值打印出來(lái),而不是直接傳遞m.budget來(lái)打印。這和你訪問(wèn)一個(gè)property相似,Python自動(dòng)調(diào)用一個(gè)方法,同時(shí)返回結(jié)果。

__get__接收2個(gè)參數(shù):一個(gè)是點(diǎn)號(hào)左邊的實(shí)例對(duì)象(在這里,就是m.budget中的m),另一個(gè)是這個(gè)實(shí)例的類(lèi)型(Movie)。在一些Python文檔中,Movie被稱(chēng)作描述符的所有者(owner)。如果我們需要訪問(wèn)Movie.budget,Python將會(huì)調(diào)用Movie.budget.__get__(None, Movie)??梢钥吹剑谝粋€(gè)參數(shù)要么是所有者的實(shí)例,要么是None。這些輸入?yún)?shù)可能看起來(lái)很怪,但是這里它們告訴了你描述符屬于哪個(gè)對(duì)象的一部分。當(dāng)我們看到NonNegative類(lèi)的實(shí)現(xiàn)時(shí)這一切就合情合理了。

對(duì)描述符賦值

當(dāng)解釋器看到m.rating = 100時(shí),Python識(shí)別出rating是一個(gè)帶有set方法的描述符,于是就調(diào)用Movie.rating.__set__(m, 100)。和__get__一樣,__set__的第一個(gè)參數(shù)是點(diǎn)號(hào)左邊的類(lèi)實(shí)例(m.rating = 100中的m)。第二個(gè)參數(shù)是所賦的值(100)。

刪除描述符

為了說(shuō)明的完整,這里提一下刪除。如果你調(diào)用del m.budget,Python就會(huì)調(diào)用Movie.budget.__delete__(m)。

NonNegative類(lèi)是如何工作的?

帶著前面的困惑,我們終于要揭示NonNegative類(lèi)是如何工作的了。每個(gè)NonNegative的實(shí)例都維護(hù)著一個(gè)字典,其中保存著所有者實(shí)例和對(duì)應(yīng)數(shù)據(jù)的映射關(guān)系。當(dāng)我們調(diào)用m.budget時(shí),__get__方法會(huì)查找與m相關(guān)聯(lián)的數(shù)據(jù),并返回這個(gè)結(jié)果(如果這個(gè)值不存在,則會(huì)返回一個(gè)默認(rèn)值)。__set__采用的方式相同,但是這里會(huì)包含額外的非負(fù)檢查。我們使用WeakKeyDictionary來(lái)取代普通的字典以防止內(nèi)存泄露——我們可不想僅僅因?yàn)樗诿枋龇淖值渲芯妥屢粋€(gè)無(wú)用 的實(shí)例一直存活著。

使用描述符會(huì)有一點(diǎn)別扭。因?yàn)樗鼈冏饔糜陬?lèi)的層次上,每一個(gè)類(lèi)實(shí)例都共享同一個(gè)描述符。這就意味著對(duì)不同的實(shí)例對(duì)象而言,描述符不得不手動(dòng)地管理不同的狀態(tài),同時(shí)需要顯式的將類(lèi)實(shí)例作為第一個(gè)參數(shù)準(zhǔn)確傳遞給__get__、__set__以及__delete__方法。

我希望這個(gè)例子解釋清楚了描述符可以用來(lái)做什么——它們提供了一種方法將property的邏輯隔離到單獨(dú)的類(lèi)中來(lái)處理。如果你發(fā)現(xiàn)自己正在不同的property之間重復(fù)著相同的邏輯,那么本文也許會(huì)成為一個(gè)線索供你思考為何用描述符重構(gòu)代碼是值得一試的。

秘訣和陷阱

把描述符放在類(lèi)的層次上(class level)

為了讓描述符能夠正常工作,它們必須定義在類(lèi)的層次上。如果你不這么做,那么Python無(wú)法自動(dòng)為你調(diào)用__get__和__set__方法。

復(fù)制代碼 代碼如下:

class Broken(object):
    y = NonNegative(5)
    def __init__(self):
        self.x = NonNegative(0)  # NOT a good descriptor
b = Broken()
print "X is %s, Y is %s" % (b.x, b.y)
X is <__main__.NonNegative object at 0x10432c250>, Y is 5

可以看到,訪問(wèn)類(lèi)層次上的描述符y可以自動(dòng)調(diào)用__get__。但是訪問(wèn)實(shí)例層次上的描述符x只會(huì)返回描述符本身,真是魔法一般的存在啊。

確保實(shí)例的數(shù)據(jù)只屬于實(shí)例本身

你可能會(huì)像這樣編寫(xiě)NonNegative描述符:

復(fù)制代碼 代碼如下:

class BrokenNonNegative(object):
    def __init__(self, default):
        self.value = default
    def __get__(self, instance, owner):
        return self.value
    def __set__(self, instance, value):
        if value < 0:
            raise ValueError("Negative value not allowed: %s" % value)
        self.value = value
class Foo(object):
    bar = BrokenNonNegative(5)
f = Foo()
try:
    f.bar = -1
except ValueError:
    print "Caught the invalid assignment"
Caught the invalid assignment

這么做看起來(lái)似乎能正常工作。但這里的問(wèn)題就在于所有Foo的實(shí)例都共享相同的bar,這會(huì)產(chǎn)生一些令人痛苦的結(jié)果:

復(fù)制代碼 代碼如下:

class Foo(object):
    bar = BrokenNonNegative(5)
f = Foo()
g = Foo()
print "f.bar is %s\ng.bar is %s" % (f.bar, g.bar)
print "Setting f.bar to 10"
f.bar = 10
print "f.bar is %s\ng.bar is %s" % (f.bar, g.bar)  #ouch
f.bar is 5
g.bar is 5
Setting f.bar to 10
f.bar is 10
g.bar is 10

這就是為什么我們要在NonNegative中使用數(shù)據(jù)字典的原因。__get__和__set__的第一個(gè)參數(shù)告訴我們需要關(guān)心哪一個(gè)實(shí)例。NonNegative使用這個(gè)參數(shù)作為字典的key,為每一個(gè)Foo實(shí)例單獨(dú)保存一份數(shù)據(jù)。

復(fù)制代碼 代碼如下:

class Foo(object):
    bar = NonNegative(5)
f = Foo()
g = Foo()
print "f.bar is %s\ng.bar is %s" % (f.bar, g.bar)
print "Setting f.bar to 10"
f.bar = 10
print "f.bar is %s\ng.bar is %s" % (f.bar, g.bar)  #better
f.bar is 5
g.bar is 5
Setting f.bar to 10
f.bar is 10
g.bar is 5

這就是描述符最令人感到別扭的地方(坦白的說(shuō),我不理解為什么Python不讓你在實(shí)例的層次上定義描述符,并且總是需要將實(shí)際的處理分發(fā)給get和set。這么做行不通一定是有原因的)

注意不可哈希的描述符所有者

NonNegative類(lèi)使用了一個(gè)字典來(lái)單獨(dú)保存專(zhuān)屬于實(shí)例的數(shù)據(jù)。這個(gè)一般來(lái)說(shuō)是沒(méi)問(wèn)題的,除非你用到了不可哈希(unhashable)的對(duì)象:

復(fù)制代碼 代碼如下:

class MoProblems(list):  #you can't use lists as dictionary keys
    x = NonNegative(5)
m = MoProblems()
print m.x  # womp womp
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-8-dd73b177bd8d> in <module>()
      3
      4 m = MoProblems()
----> 5 print m.x  # womp womp
<ipython-input-3-6671804ce5d5> in __get__(self, instance, owner)
      9         # instance = x
     10         # owner = type(x)
---> 11         return self.data.get(instance, self.default)
     12
     13     def __set__(self, instance, value):
TypeError: unhashable type: 'MoProblems'

因?yàn)镸oProblems的實(shí)例(list的子類(lèi))是不可哈希的,因此它們不能為MoProblems.x用做數(shù)據(jù)字典的key。有一些方法可以規(guī)避這個(gè)問(wèn)題,但是都不完美。最好的方法可能就是給你的描述符加標(biāo)簽了。

復(fù)制代碼 代碼如下:

class Descriptor(object):
    def __init__(self, label):
        self.label = label
    def __get__(self, instance, owner):
        print '__get__', instance, owner
        return instance.__dict__.get(self.label)
    def __set__(self, instance, value):
        print '__set__'
        instance.__dict__[self.label] = value
class Foo(list):
    x = Descriptor('x')
    y = Descriptor('y')
f = Foo()
f.x = 5
print f.x
__set__
__get__ [] <class '__main__.Foo'>
5

這種方法依賴(lài)于Python的方法解析順序(即,MRO)。我們給Foo中的每個(gè)描述符加上一個(gè)標(biāo)簽名,名稱(chēng)和我們賦值給描述符的變量名相同,比如x = Descriptor(‘x’)。之后,描述符將特定于實(shí)例的數(shù)據(jù)保存在f.__dict__['x']中。這個(gè)字典條目通常是當(dāng)我們請(qǐng)求f.x時(shí)Python給出的返回值。然而,由于Foo.x 是一個(gè)描述符,Python不能正常的使用f.__dict__[‘x’],但是描述符可以安全的在這里存儲(chǔ)數(shù)據(jù)。只是要記住,不要在別的地方也給這個(gè)描述符添加標(biāo)簽。

復(fù)制代碼 代碼如下:

class Foo(object):
    x = Descriptor('y')
f = Foo()
f.x = 5
print f.x
f.y = 4    #oh no!
print f.x
__set__
__get__ <__main__.Foo object at 0x10432c810> <class '__main__.Foo'>
5
__get__ <__main__.Foo object at 0x10432c810> <class '__main__.Foo'>
4

我不喜歡這種方式,因?yàn)檫@樣的代碼很脆弱也有很多微妙之處。但這個(gè)方法的確很普遍,可以用在不可哈希的所有者類(lèi)上。David Beazley在他的書(shū)中用到了這個(gè)方法。

在元類(lèi)中使用帶標(biāo)簽的描述符

由于描述符的標(biāo)簽名和賦給它的變量名相同,所以有人使用元類(lèi)來(lái)自動(dòng)處理這個(gè)簿記(bookkeeping)任務(wù)。

復(fù)制代碼 代碼如下:

class Descriptor(object):
    def __init__(self):
        #notice we aren't setting the label here
        self.label = None
    def __get__(self, instance, owner):
        print '__get__. Label = %s' % self.label
        return instance.__dict__.get(self.label, None)
    def __set__(self, instance, value):
        print '__set__'
        instance.__dict__[self.label] = value
class DescriptorOwner(type):
    def __new__(cls, name, bases, attrs):
        # find all descriptors, auto-set their labels
        for n, v in attrs.items():
            if isinstance(v, Descriptor):
                v.label = n
        return super(DescriptorOwner, cls).__new__(cls, name, bases, attrs)
class Foo(object):
    __metaclass__ = DescriptorOwner
    x = Descriptor()
f = Foo()
f.x = 10
print f.x
__set__
__get__. Label = x
10

我不會(huì)去解釋有關(guān)元類(lèi)的細(xì)節(jié)——參考文獻(xiàn)中David Beazley已經(jīng)在他的文章中解釋的很清楚了。 需要指出的是元類(lèi)自動(dòng)的為描述符添加標(biāo)簽,并且和賦給描述符的變量名字相匹配。

盡管這樣解決了描述符的標(biāo)簽和變量名不一致的問(wèn)題,但是卻引入了復(fù)雜的元類(lèi)。雖然我很懷疑,但是你可以自行判斷這么做是否值得。

訪問(wèn)描述符的方法

描述符僅僅是類(lèi),也許你想要為它們?cè)黾右恍┓椒āEe個(gè)例子,描述符是一個(gè)用來(lái)回調(diào)property的很好的手段。比如我們想要一個(gè)類(lèi)的某個(gè)部分的狀態(tài)發(fā)生變化時(shí)就立刻通知我們。下面的大部分代碼是用來(lái)做這個(gè)的:

復(fù)制代碼 代碼如下:

class CallbackProperty(object):
    """A property that will alert observers when upon updates"""
    def __init__(self, default=None):
        self.data = WeakKeyDictionary()
        self.default = default
        self.callbacks = WeakKeyDictionary()
    def __get__(self, instance, owner):
        return self.data.get(instance, self.default)
    def __set__(self, instance, value):       
        for callback in self.callbacks.get(instance, []):
            # alert callback function of new value
            callback(value)
        self.data[instance] = value
    def add_callback(self, instance, callback):
        """Add a new function to call everytime the descriptor updates"""
        #but how do we get here?!?!
        if instance not in self.callbacks:
            self.callbacks[instance] = []
        self.callbacks[instance].append(callback)
class BankAccount(object):
    balance = CallbackProperty(0)
def low_balance_warning(value):
    if value < 100:
        print "You are poor"
ba = BankAccount()
# will not work -- try it
#ba.balance.add_callback(ba, low_balance_warning)

這是一個(gè)很有吸引力的模式——我們可以自定義回調(diào)函數(shù)用來(lái)響應(yīng)一個(gè)類(lèi)中的狀態(tài)變化,而且完全無(wú)需修改這個(gè)類(lèi)的代碼。這樣做可真是替人分憂解難呀?,F(xiàn)在,我們所要做的就是調(diào)用ba.balance.add_callback(ba, low_balance_warning),以使得每次balance變化時(shí)low_balance_warning都會(huì)被調(diào)用。

但是我們是如何做到的呢?當(dāng)我們?cè)噲D訪問(wèn)它們時(shí),描述符總是會(huì)調(diào)用__get__。就好像add_callback方法是無(wú)法觸及的一樣!其實(shí)關(guān)鍵在于利用了一種特殊的情況,即,當(dāng)從類(lèi)的層次訪問(wèn)時(shí),__get__方法的第一個(gè)參數(shù)是None。

復(fù)制代碼 代碼如下:

class CallbackProperty(object):
    """A property that will alert observers when upon updates"""
    def __init__(self, default=None):
        self.data = WeakKeyDictionary()
        self.default = default
        self.callbacks = WeakKeyDictionary()
    def __get__(self, instance, owner):
        if instance is None:
            return self       
        return self.data.get(instance, self.default)
    def __set__(self, instance, value):
        for callback in self.callbacks.get(instance, []):
            # alert callback function of new value
            callback(value)
        self.data[instance] = value
    def add_callback(self, instance, callback):
        """Add a new function to call everytime the descriptor within instance updates"""
        if instance not in self.callbacks:
            self.callbacks[instance] = []
        self.callbacks[instance].append(callback)
class BankAccount(object):
    balance = CallbackProperty(0)
def low_balance_warning(value):
    if value < 100:
        print "You are now poor"
ba = BankAccount()
BankAccount.balance.add_callback(ba, low_balance_warning)
ba.balance = 5000
print "Balance is %s" % ba.balance
ba.balance = 99
print "Balance is %s" % ba.balance
Balance is 5000
You are now poor
Balance is 99

結(jié)語(yǔ)

希望你現(xiàn)在對(duì)描述符是什么和它們的適用場(chǎng)景有了一個(gè)認(rèn)識(shí)。前進(jìn)吧!

相關(guān)文章

  • Python趣味爬蟲(chóng)之用Python實(shí)現(xiàn)智慧校園一鍵評(píng)教

    Python趣味爬蟲(chóng)之用Python實(shí)現(xiàn)智慧校園一鍵評(píng)教

    你還在為智慧校園每周的評(píng)教而苦惱嗎?今天我來(lái)幫你解放雙手,用Python實(shí)現(xiàn)一鍵評(píng)教,從此生活無(wú)憂無(wú)慮,文中有非常詳細(xì)的代碼示例,需要的朋友可以參考下
    2021-05-05
  • Django ORM數(shù)據(jù)庫(kù)操作處理全面指南

    Django ORM數(shù)據(jù)庫(kù)操作處理全面指南

    本文深度探討Django ORM的概念、基礎(chǔ)使用、進(jìn)階操作以及詳細(xì)解析在實(shí)際使用中如何處理數(shù)據(jù)庫(kù)操作,同時(shí),我們還討論了模型深入理解,如何進(jìn)行CRUD操作,并且深化理解到數(shù)據(jù)庫(kù)遷移等高級(jí)主題
    2023-09-09
  • python簡(jiǎn)單實(shí)現(xiàn)AES加密和解密

    python簡(jiǎn)單實(shí)現(xiàn)AES加密和解密

    這篇文章主要為大家詳細(xì)介紹了python簡(jiǎn)單實(shí)現(xiàn)AES加密和解密,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2019-03-03
  • pandas實(shí)現(xiàn)導(dǎo)出數(shù)據(jù)的四種方式

    pandas實(shí)現(xiàn)導(dǎo)出數(shù)據(jù)的四種方式

    這篇文章主要介紹了pandas實(shí)現(xiàn)導(dǎo)出數(shù)據(jù)的四種方式,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2020-12-12
  • python中的super如何使用

    python中的super如何使用

    這篇文章主要介紹了python中的super,python中的super,名為超類(lèi),可以簡(jiǎn)單的理解為執(zhí)行父類(lèi)的__init__函數(shù),本文就著重看下super的具體作用,需要的朋友可以參考下
    2022-03-03
  • Python實(shí)現(xiàn)PS濾鏡的旋轉(zhuǎn)模糊功能示例

    Python實(shí)現(xiàn)PS濾鏡的旋轉(zhuǎn)模糊功能示例

    這篇文章主要介紹了Python實(shí)現(xiàn)PS濾鏡的旋轉(zhuǎn)模糊功能,涉及Python基于skimage庫(kù)針對(duì)圖片進(jìn)行旋轉(zhuǎn)與模糊化處理的相關(guān)操作技巧,需要的朋友可以參考下
    2018-01-01
  • Python數(shù)據(jù)分析之分析千萬(wàn)級(jí)淘寶數(shù)據(jù)

    Python數(shù)據(jù)分析之分析千萬(wàn)級(jí)淘寶數(shù)據(jù)

    網(wǎng)購(gòu)已經(jīng)成為人們生活不可或缺的一部分,本次項(xiàng)目基于淘寶app平臺(tái)數(shù)據(jù),通過(guò)相關(guān)指標(biāo)對(duì)用戶(hù)行為進(jìn)行分析,從而探索用戶(hù)相關(guān)行為模式。感興趣的可以學(xué)習(xí)一下
    2022-03-03
  • Python 循環(huán)語(yǔ)句之 while,for語(yǔ)句詳解

    Python 循環(huán)語(yǔ)句之 while,for語(yǔ)句詳解

    Python中有兩種循環(huán),分別為:for循環(huán)和while循環(huán)。 for循環(huán)可以遍歷任何序列的項(xiàng)目,如一個(gè)列表或者一個(gè)字符串。while 語(yǔ)句用于循環(huán)執(zhí)行程序,即在某條件下,循環(huán)執(zhí)行某段程序,以處理需要重復(fù)處理的相同任務(wù)。
    2018-04-04
  • python初學(xué)者,用python實(shí)現(xiàn)基本的學(xué)生管理系統(tǒng)(python3)代碼實(shí)例

    python初學(xué)者,用python實(shí)現(xiàn)基本的學(xué)生管理系統(tǒng)(python3)代碼實(shí)例

    這篇文章主要介紹了用python實(shí)現(xiàn)學(xué)生管理系統(tǒng),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2019-04-04
  • 淺談Keras參數(shù) input_shape、input_dim和input_length用法

    淺談Keras參數(shù) input_shape、input_dim和input_length用法

    這篇文章主要介紹了淺談Keras參數(shù) input_shape、input_dim和input_length用法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2020-06-06

最新評(píng)論

资中县| 元谋县| 永泰县| 齐齐哈尔市| 竹山县| 临汾市| 扎兰屯市| 慈溪市| 南康市| 彭州市| 永清县| 尚义县| 辛集市| 玉屏| 民县| 盐亭县| 肥城市| 定陶县| 顺昌县| 泉州市| 清流县| 南和县| 塔城市| 潮安县| 杨浦区| 富民县| 紫金县| 额济纳旗| 潢川县| 庆阳市| 天柱县| 双桥区| 舒城县| 乐安县| 盐津县| 宁化县| 晋宁县| 涞水县| 竹山县| 洛隆县| 马鞍山市|