国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專(zhuān)欄INFORMATION COLUMN

介紹Python的魔術(shù)方法 - Magic Method

animabear / 3463人閱讀

摘要:所以準(zhǔn)確來(lái)說(shuō)是和共同構(gòu)成了構(gòu)造函數(shù)是用來(lái)創(chuàng)建類(lèi)并返回這個(gè)類(lèi)的實(shí)例而只是將傳入的參數(shù)來(lái)初始化該實(shí)例在創(chuàng)建一個(gè)實(shí)例的過(guò)程中必定會(huì)被調(diào)用但就不一定,比如通過(guò)的方式反序列化一個(gè)實(shí)例時(shí)就不會(huì)調(diào)用。

前言

在Python中,所有以__雙下劃線(xiàn)包起來(lái)的方法,都統(tǒng)稱(chēng)為"魔術(shù)方法"。比如我們接觸最多的__init__.

有些魔術(shù)方法,我們可能以后一輩子都不會(huì)再遇到了,這里也就只是簡(jiǎn)單介紹下;

而有些魔術(shù)方法,巧妙使用它可以構(gòu)造出非常優(yōu)美的代碼,比如將復(fù)雜的邏輯封裝成簡(jiǎn)單的API。

本文編輯的思路借鑒自Rafe Kettler的這篇博客: A Guide to Python Magic Methods,并補(bǔ)充了一些代碼示例。

介紹的順序大概是:常見(jiàn)的先介紹,越少見(jiàn)的越靠后講。

本文中用到的代碼示例,可以在我的github下載到。

構(gòu)造和初始化

__init__我們很熟悉了,它在對(duì)象初始化的時(shí)候調(diào)用,我們一般將它理解為"構(gòu)造函數(shù)".

實(shí)際上, 當(dāng)我們調(diào)用x = SomeClass()的時(shí)候調(diào)用,__init__并不是第一個(gè)執(zhí)行的, __new__才是。所以準(zhǔn)確來(lái)說(shuō),是__new____init__共同構(gòu)成了"構(gòu)造函數(shù)".

__new__是用來(lái)創(chuàng)建類(lèi)并返回這個(gè)類(lèi)的實(shí)例, 而__init__只是將傳入的參數(shù)來(lái)初始化該實(shí)例.

__new__在創(chuàng)建一個(gè)實(shí)例的過(guò)程中必定會(huì)被調(diào)用,但__init__就不一定,比如通過(guò)pickle.load的方式反序列化一個(gè)實(shí)例時(shí)就不會(huì)調(diào)用__init__

__new__方法總是需要返回該類(lèi)的一個(gè)實(shí)例,而__init__不能返回除了None的任何值。比如下面例子:

class Foo(object):

    def __init__(self):
        print "foo __init__"
        return None  # 必須返回None,否則拋TypeError

    def __del__(self):
        print "foo __del__"

實(shí)際中,你很少會(huì)用到__new__,除非你希望能夠控制類(lèi)的創(chuàng)建。
如果要講解__new__,往往需要牽扯到metaclass(元類(lèi))的介紹。
如果你有興趣深入,可以參考我的另一篇博客: 理解Python的metaclass

對(duì)于__new__的重載,Python文檔中也有了詳細(xì)的介紹。

在對(duì)象的生命周期結(jié)束時(shí), __del__會(huì)被調(diào)用,可以將__del__理解為"析構(gòu)函數(shù)".
__del__定義的是當(dāng)一個(gè)對(duì)象進(jìn)行垃圾回收時(shí)候的行為。

有一點(diǎn)容易被人誤解, 實(shí)際上,x.__del__() 并不是對(duì)于del x的實(shí)現(xiàn),但是往往執(zhí)行del x時(shí)會(huì)調(diào)用x.__del__().

怎么來(lái)理解這句話(huà)呢? 繼續(xù)用上面的Foo類(lèi)的代碼為例:

foo = Foo()
foo.__del__()
print foo
del foo
print foo  # NameError, foo is not defined

如果調(diào)用了foo.__del__(),對(duì)象本身仍然存在. 但是調(diào)用了del foo, 就再也沒(méi)有foo這個(gè)對(duì)象了.

請(qǐng)注意,如果解釋器退出的時(shí)候?qū)ο筮€存在,就不能保證 __del__ 被確切的執(zhí)行了。所以__del__并不能替代良好的編程習(xí)慣。
比如,在處理socket時(shí),及時(shí)關(guān)閉結(jié)束的連接。

屬性訪(fǎng)問(wèn)控制

總有人要吐槽Python缺少對(duì)于類(lèi)的封裝,比如希望Python能夠定義私有屬性,然后提供公共可訪(fǎng)問(wèn)的getter和 setter。Python其實(shí)可以通過(guò)魔術(shù)方法來(lái)實(shí)現(xiàn)封裝。

__getattr__(self, name)

該方法定義了你試圖訪(fǎng)問(wèn)一個(gè)不存在的屬性時(shí)的行為。因此,重載該方法可以實(shí)現(xiàn)捕獲錯(cuò)誤拼寫(xiě)然后進(jìn)行重定向, 或者對(duì)一些廢棄的屬性進(jìn)行警告。

__setattr__(self, name, value)

__setattr__ 是實(shí)現(xiàn)封裝的解決方案,它定義了你對(duì)屬性進(jìn)行賦值和修改操作時(shí)的行為。
不管對(duì)象的某個(gè)屬性是否存在,它都允許你為該屬性進(jìn)行賦值,因此你可以為屬性的值進(jìn)行自定義操作。有一點(diǎn)需要注意,實(shí)現(xiàn)__setattr__時(shí)要避免"無(wú)限遞歸"的錯(cuò)誤,下面的代碼示例中會(huì)提到。

__delattr__(self, name)

__delattr____setattr__很像,只是它定義的是你刪除屬性時(shí)的行為。實(shí)現(xiàn)__delattr__是同時(shí)要避免"無(wú)限遞歸"的錯(cuò)誤。

__getattribute__(self, name)

__getattribute__定義了你的屬性被訪(fǎng)問(wèn)時(shí)的行為,相比較,__getattr__只有該屬性不存在時(shí)才會(huì)起作用。
因此,在支持__getattribute__的Python版本,調(diào)用__getattr__前必定會(huì)調(diào)用 __getattribute____getattribute__同樣要避免"無(wú)限遞歸"的錯(cuò)誤。
需要提醒的是,最好不要嘗試去實(shí)現(xiàn)__getattribute__,因?yàn)楹苌僖?jiàn)到這種做法,而且很容易出bug。

例子說(shuō)明__setattr__的無(wú)限遞歸錯(cuò)誤:

def __setattr__(self, name, value):
    self.name = value
    # 每一次屬性賦值時(shí), __setattr__都會(huì)被調(diào)用,因此不斷調(diào)用自身導(dǎo)致無(wú)限遞歸了。

因此正確的寫(xiě)法應(yīng)該是:

def __setattr__(self, name, value):
    self.__dict__[name] = value

__delattr__如果在其實(shí)現(xiàn)中出現(xiàn)del self.name 這樣的代碼也會(huì)出現(xiàn)"無(wú)限遞歸"錯(cuò)誤,這是一樣的原因。

下面的例子很好的說(shuō)明了上面介紹的4個(gè)魔術(shù)方法的調(diào)用情況:

class Access(object):

    def __getattr__(self, name):
        print "__getattr__"
        return super(Access, self).__getattr__(name)

    def __setattr__(self, name, value):
        print "__setattr__"
        return super(Access, self).__setattr__(name, value)

    def __delattr__(self, name):
        print "__delattr__"
        return super(Access, self).__delattr__(name)

    def __getattribute__(self, name):
        print "__getattribute__"
        return super(Access, self).__getattribute__(name)

access = Access()
access.attr1 = True  # __setattr__調(diào)用
access.attr1  # 屬性存在,只有__getattribute__調(diào)用
try:
    access.attr2  # 屬性不存在, 先調(diào)用__getattribute__, 后調(diào)用__getattr__
except AttributeError:
    pass
del access.attr1  # __delattr__調(diào)用
描述器對(duì)象

我們從一個(gè)例子來(lái)入手,介紹什么是描述符,并介紹__get__, __set__, __delete__ 的使用。(放在這里介紹是為了跟上一小節(jié)介紹的魔術(shù)方法作對(duì)比)

我們知道,距離既可以用單位"米"表示,也可以用單位"英尺"表示。
現(xiàn)在我們定義一個(gè)類(lèi)來(lái)表示距離,它有兩個(gè)屬性: 米和英尺。

class Meter(object):
    """Descriptor for a meter."""
    def __init__(self, value=0.0):
        self.value = float(value)
    def __get__(self, instance, owner):
        return self.value
    def __set__(self, instance, value):
        self.value = float(value)

class Foot(object):
    """Descriptor for a foot."""
    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()

d = Distance()
print d.meter, d.foot  # 0.0, 0.0
d.meter = 1
print d.meter, d.foot  # 1.0 3.2808
d.meter = 2
print d.meter, d.foot  # 2.0 6.5616

在上面例子中,在還沒(méi)有對(duì)Distance的實(shí)例賦值前, 我們認(rèn)為meter和foot應(yīng)該是各自類(lèi)的實(shí)例對(duì)象, 但是輸出卻是數(shù)值。這是因?yàn)?b>__get__發(fā)揮了作用.

我們只是修改了meter,并且將其賦值成為int,但foot也修改了。這是__set__發(fā)揮了作用.

描述器對(duì)象(Meter、Foot)不能獨(dú)立存在, 它需要被另一個(gè)所有者類(lèi)(Distance)所持有。
描述器對(duì)象可以訪(fǎng)問(wèn)到其擁有者實(shí)例的屬性,比如例子中Foot的instance.meter

在面向?qū)ο缶幊虝r(shí),如果一個(gè)類(lèi)的屬性有相互依賴(lài)的關(guān)系時(shí),使用描述器來(lái)編寫(xiě)代碼可以很巧妙的組織邏輯。
在Django的ORM中, models.Model中的IntegerField等, 就是通過(guò)描述器來(lái)實(shí)現(xiàn)功能的。

一個(gè)類(lèi)要成為描述器,必須實(shí)現(xiàn)__get__, __set__, __delete__ 中的至少一個(gè)方法。下面簡(jiǎn)單介紹下:

__get__(self, instance, owner)

參數(shù)instance是擁有者類(lèi)的實(shí)例。參數(shù)owner是擁有者類(lèi)本身。__get__在其擁有者對(duì)其讀值的時(shí)候調(diào)用。

__set__(self, instance, value)

__set__在其擁有者對(duì)其進(jìn)行修改值的時(shí)候調(diào)用。

__delete__(self, instance)

__delete__在其擁有者對(duì)其進(jìn)行刪除的時(shí)候調(diào)用。

構(gòu)造自定義容器(Container)

在Python中,常見(jiàn)的容器類(lèi)型有: dict, tuple, list, string。
其中tuple, string是不可變?nèi)萜鳎琩ict, list是可變?nèi)萜鳌?br>可變?nèi)萜骱筒豢勺內(nèi)萜鞯膮^(qū)別在于,不可變?nèi)萜饕坏┵x值后,不可對(duì)其中的某個(gè)元素進(jìn)行修改。
比如定義了l = [1, 2, 3]t = (1, 2, 3)后, 執(zhí)行l[0] = 0是可以的,但執(zhí)行t[0] = 0則會(huì)報(bào)錯(cuò)。

如果我們要自定義一些數(shù)據(jù)結(jié)構(gòu),使之能夠跟以上的容器類(lèi)型表現(xiàn)一樣,那就需要去實(shí)現(xiàn)某些協(xié)議。

這里的協(xié)議跟其他語(yǔ)言中所謂的"接口"概念很像,一樣的需要你去實(shí)現(xiàn)才行,只不過(guò)沒(méi)那么正式而已。

如果要自定義不可變?nèi)萜黝?lèi)型,只需要定義__len____getitem__方法;
如果要自定義可變?nèi)萜黝?lèi)型,還需要在不可變?nèi)萜黝?lèi)型的基礎(chǔ)上增加定義__setitem____delitem__
如果你希望你的自定義數(shù)據(jù)結(jié)構(gòu)還支持"可迭代", 那就還需要定義__iter__

__len__(self)

需要返回?cái)?shù)值類(lèi)型,以表示容器的長(zhǎng)度。該方法在可變?nèi)萜骱筒豢勺內(nèi)萜髦斜仨殞?shí)現(xiàn)。

__getitem__(self, key)

當(dāng)你執(zhí)行self[key]的時(shí)候,調(diào)用的就是該方法。該方法在可變?nèi)萜骱筒豢勺內(nèi)萜髦幸捕急仨殞?shí)現(xiàn)。
調(diào)用的時(shí)候,如果key的類(lèi)型錯(cuò)誤,該方法應(yīng)該拋出TypeError;
如果沒(méi)法返回key對(duì)應(yīng)的數(shù)值時(shí),該方法應(yīng)該拋出ValueError。

__setitem__(self, key, value)

當(dāng)你執(zhí)行self[key] = value時(shí),調(diào)用的是該方法。

__delitem__(self, key)

當(dāng)你執(zhí)行del self[key]的時(shí)候,調(diào)用的是該方法。

__iter__(self)

該方法需要返回一個(gè)迭代器(iterator)。當(dāng)你執(zhí)行for x in container: 或者使用iter(container)時(shí),該方法被調(diào)用。

__reversed__(self)

如果想要該數(shù)據(jù)結(jié)構(gòu)被內(nèi)建函數(shù)reversed()支持,就還需要實(shí)現(xiàn)該方法。

__contains__(self, item)

如果定義了該方法,那么在執(zhí)行item in container 或者 item not in container時(shí)該方法就會(huì)被調(diào)用。
如果沒(méi)有定義,那么Python會(huì)迭代容器中的元素來(lái)一個(gè)一個(gè)比較,從而決定返回True或者False。

__missing__(self, key)

dict字典類(lèi)型會(huì)有該方法,它定義了key如果在容器中找不到時(shí)觸發(fā)的行為。
比如d = {"a": 1}, 當(dāng)你執(zhí)行d[notexist]時(shí),d.__missing__("notexist")就會(huì)被調(diào)用。

下面舉例,使用上面講的魔術(shù)方法來(lái)實(shí)現(xiàn)Haskell語(yǔ)言中的一個(gè)數(shù)據(jù)結(jié)構(gòu)。

# -*- coding: utf-8 -*-
class FunctionalList:
    """ 實(shí)現(xiàn)了內(nèi)置類(lèi)型list的功能,并豐富了一些其他方法: head, tail, init, last, drop, take"""

    def __init__(self, values=None):
        if values is None:
            self.values = []
        else:
            self.values = values

    def __len__(self):
        return len(self.values)

    def __getitem__(self, key):
        return self.values[key]

    def __setitem__(self, key, value):
        self.values[key] = value

    def __delitem__(self, key):
        del self.values[key]

    def __iter__(self):
        return iter(self.values)

    def __reversed__(self):
        return FunctionalList(reversed(self.values))

    def append(self, value):
        self.values.append(value)
    def head(self):
        # 獲取第一個(gè)元素
        return self.values[0]
    def tail(self):
        # 獲取第一個(gè)元素之后的所有元素
        return self.values[1:]
    def init(self):
        # 獲取最后一個(gè)元素之前的所有元素
        return self.values[:-1]
    def last(self):
        # 獲取最后一個(gè)元素
        return self.values[-1]
    def drop(self, n):
        # 獲取所有元素,除了前N個(gè)
        return self.values[n:]
    def take(self, n):
        # 獲取前N個(gè)元素
        return self.values[:n]

我們?cè)倥e個(gè)例子,實(shí)現(xiàn)Perl語(yǔ)言的AutoVivification,它會(huì)在你每次引用一個(gè)值未定義的屬性時(shí)為你自動(dòng)創(chuàng)建數(shù)組或者字典。

class AutoVivification(dict):
    """Implementation of perl"s autovivification feature."""
    def __missing__(self, key):
        value = self[key] = type(self)()
        return value

weather = AutoVivification()
weather["china"]["guangdong"]["shenzhen"] = "sunny"
weather["china"]["hubei"]["wuhan"] = "windy"
weather["USA"]["California"]["Los Angeles"] = "sunny"
print weather

# 結(jié)果輸出:{"china": {"hubei": {"wuhan": "windy"}, "guangdong": {"shenzhen": "sunny"}}, "USA":    {"California": {"Los Angeles": "sunny"}}}

在Python中,關(guān)于自定義容器的實(shí)現(xiàn)還有更多實(shí)用的例子,但只有很少一部分能夠集成在Python標(biāo)準(zhǔn)庫(kù)中,比如Counter, OrderedDict等

上下文管理

with聲明是從Python2.5開(kāi)始引進(jìn)的關(guān)鍵詞。你應(yīng)該遇過(guò)這樣子的代碼:

with open("foo.txt") as bar:
    # do something with bar

在with聲明的代碼段中,我們可以做一些對(duì)象的開(kāi)始操作和清除操作,還能對(duì)異常進(jìn)行處理。
這需要實(shí)現(xiàn)兩個(gè)魔術(shù)方法: __enter____exit__

__enter__(self)

__enter__會(huì)返回一個(gè)值,并賦值給as關(guān)鍵詞之后的變量。在這里,你可以定義代碼段開(kāi)始的一些操作。

__exit__(self, exception_type, exception_value, traceback)

__exit__定義了代碼段結(jié)束后的一些操作,可以這里執(zhí)行一些清除操作,或者做一些代碼段結(jié)束后需要立即執(zhí)行的命令,比如文件的關(guān)閉,socket斷開(kāi)等。如果代碼段成功結(jié)束,那么exception_type, exception_value, traceback 三個(gè)參數(shù)傳進(jìn)來(lái)時(shí)都將為None。如果代碼段拋出異常,那么傳進(jìn)來(lái)的三個(gè)參數(shù)將分別為: 異常的類(lèi)型,異常的值,異常的追蹤棧。
如果__exit__返回True, 那么with聲明下的代碼段的一切異常將會(huì)被屏蔽。
如果__exit__返回None, 那么如果有異常,異常將正常拋出,這時(shí)候with的作用將不會(huì)顯現(xiàn)出來(lái)。

舉例說(shuō)明:

這該示例中,IndexError始終會(huì)被隱藏,而TypeError始終會(huì)拋出。

class DemoManager(object):

    def __enter__(self):
        pass

    def __exit__(self, ex_type, ex_value, ex_tb):
        if ex_type is IndexError:
            print ex_value.__class__
            return True
        if ex_type is TypeError:
            print ex_value.__class__
            return  # return None

with DemoManager() as nothing:
    data = [1, 2, 3]
    data[4]  # raise IndexError, 該異常被__exit__處理了

with DemoManager() as nothing:
    data = [1, 2, 3]
    data["a"]  # raise TypeError, 該異常沒(méi)有被__exit__處理

"""
輸出:


Traceback (most recent call last):
  ...
"""
對(duì)象的序列化

Python對(duì)象的序列化操作是pickling進(jìn)行的。pickling非常的重要,以至于Python對(duì)此有多帶帶的模塊pickle,還有一些相關(guān)的魔術(shù)方法。使用pickling, 你可以將數(shù)據(jù)存儲(chǔ)在文件中,之后又從文件中進(jìn)行恢復(fù)。

下面舉例來(lái)描述pickle的操作。從該例子中也可以看出,如果通過(guò)pickle.load 初始化一個(gè)對(duì)象, 并不會(huì)調(diào)用__init__方法。

# -*- coding: utf-8 -*-
from datetime import datetime
import pickle

class Distance(object):

    def __init__(self, meter):
        print "distance __init__"
        self.meter = meter

data = {
    "foo": [1, 2, 3],
    "bar": ("Hello", "world!"),
    "baz": True,
    "dt": datetime(2016, 10, 01),
    "distance": Distance(1.78),
}
print "before dump:", data
with open("data.pkl", "wb") as jar:
    pickle.dump(data, jar)  # 將數(shù)據(jù)存儲(chǔ)在文件中

del data
print "data is deleted!"

with open("data.pkl", "rb") as jar:
    data = pickle.load(jar)  # 從文件中恢復(fù)數(shù)據(jù)
print "after load:", data

值得一提,從其他文件進(jìn)行pickle.load操作時(shí),需要注意有惡意代碼的可能性。另外,Python的各個(gè)版本之間,pickle文件可能是互不兼容的。

pickling并不是Python的內(nèi)建類(lèi)型,它支持所有實(shí)現(xiàn)pickle協(xié)議(可理解為接口)的類(lèi)。pickle協(xié)議有以下幾個(gè)可選方法來(lái)自定義Python對(duì)象的行為。

__getinitargs__(self)

如果你希望unpickle時(shí),__init__方法能夠調(diào)用,那么就需要定義__getinitargs__, 該方法需要返回一系列參數(shù)的元組,這些參數(shù)就是傳給__init__的參數(shù)。

該方法只對(duì)old-style class有效。所謂old-style class,指的是不繼承自任何對(duì)象的類(lèi),往往定義時(shí)這樣表示: class A:, 而非class A(object):

__getnewargs__(self)

__getinitargs__很類(lèi)似,只不過(guò)返回的參數(shù)元組將傳值給__new__

__getstate__(self)

在調(diào)用pickle.dump時(shí),默認(rèn)是對(duì)象的__dict__屬性被存儲(chǔ),如果你要修改這種行為,可以在__getstate__方法中返回一個(gè)state。state將在調(diào)用pickle.load時(shí)傳值給__setstate__

__setstate__(self, state)

一般來(lái)說(shuō),定義了__getstate__,就需要相應(yīng)地定義__setstate__來(lái)對(duì)__getstate__返回的state進(jìn)行處理。

__reduce__(self)

如果pickle的數(shù)據(jù)包含了自定義的擴(kuò)展類(lèi)(比如使用C語(yǔ)言實(shí)現(xiàn)的Python擴(kuò)展類(lèi))時(shí),就需要通過(guò)實(shí)現(xiàn)__reduce__方法來(lái)控制行為了。由于使用過(guò)于生僻,這里就不展開(kāi)繼續(xù)講解了。

令人容易混淆的是,我們知道, reduce()是Python的一個(gè)內(nèi)建函數(shù), 需要指出__reduce__并非定義了reduce()的行為,二者沒(méi)有關(guān)系。

__reduce_ex__(self)

__reduce_ex__ 是為了兼容性而存在的, 如果定義了__reduce_ex__, 它將代替__reduce__ 執(zhí)行。

下面的代碼示例很有意思,我們定義了一個(gè)類(lèi)Slate(中文是板巖的意思)。這個(gè)類(lèi)能夠記錄歷史上每次寫(xiě)入給它的值,但每次pickle.dump時(shí)當(dāng)前值就會(huì)被清空,僅保留了歷史。

# -*- coding: utf-8 -*-
import pickle
import time

class Slate:
    """Class to store a string and a changelog, and forget its value when pickled."""
    def __init__(self, value):
        self.value = value
        self.last_change = time.time()
        self.history = []

    def change(self, new_value):
        # 修改value, 將上次的valeu記錄在history
        self.history.append((self.last_change, self.value))
        self.value = new_value
        self.last_change = time.time()

    def print_changes(self):
        print "Changelog for Slate object:"
        for k, v in self.history:
            print "%s    %s" % (k, v)

    def __getstate__(self):
        # 故意不返回self.value和self.last_change,
        # 以便每次unpickle時(shí)清空當(dāng)前的狀態(tài),僅僅保留history
        return self.history

    def __setstate__(self, state):
        self.history = state
        self.value, self.last_change = None, None

slate = Slate(0)
time.sleep(0.5)
slate.change(100)
time.sleep(0.5)
slate.change(200)
slate.change(300)
slate.print_changes()  # 與下面的輸出歷史對(duì)比
with open("slate.pkl", "wb") as jar:
    pickle.dump(slate, jar)
del slate  # delete it
with open("slate.pkl", "rb") as jar:
    slate = pickle.load(jar)
print "current value:", slate.value  # None
print slate.print_changes()  # 輸出歷史記錄與上面一致
運(yùn)算符相關(guān)的魔術(shù)方法

運(yùn)算符相關(guān)的魔術(shù)方法實(shí)在太多了,也很好理解,不打算多講。在其他語(yǔ)言里,也有重載運(yùn)算符的操作,所以我們對(duì)這些魔術(shù)方法已經(jīng)很了解了。

比較運(yùn)算符

__cmp__(self, other)

如果該方法返回負(fù)數(shù),說(shuō)明self < other; 返回正數(shù),說(shuō)明self > other; 返回0說(shuō)明self == other
強(qiáng)烈不推薦來(lái)定義__cmp__, 取而代之, 最好分別定義__lt__等方法從而實(shí)現(xiàn)比較功能。
__cmp__在Python3中被廢棄了。

__eq__(self, other)

定義了比較操作符==的行為.

__ne__(self, other)

定義了比較操作符!=的行為.

__lt__(self, other)

定義了比較操作符<的行為.

__gt__(self, other)

定義了比較操作符>的行為.

__le__(self, other)

定義了比較操作符<=的行為.

__ge__(self, other)

定義了比較操作符>=的行為.

下面我們定義一種類(lèi)型Word, 它會(huì)使用單詞的長(zhǎng)度來(lái)進(jìn)行大小的比較, 而不是采用str的比較方式。
但是為了避免 Word("bar") == Word("foo") 這種違背直覺(jué)的情況出現(xiàn),并沒(méi)有定義__eq__, 因此Word會(huì)使用它的父類(lèi)(str)中的__eq__來(lái)進(jìn)行比較。

下面的例子中也可以看出: 在編程語(yǔ)言中, 如果a >=b and a <= b, 并不能推導(dǎo)出a == b這樣的結(jié)論。

# -*- coding: utf-8 -*-
class Word(str):
    """存儲(chǔ)單詞的類(lèi),定義比較單詞的幾種方法"""
    def __new__(cls, word):
        # 注意我們必須要用到__new__方法,因?yàn)閟tr是不可變類(lèi)型
        # 所以我們必須在創(chuàng)建的時(shí)候?qū)⑺跏蓟?        if " " in word:
            print "Value contains spaces. Truncating to first space."
            word = word[:word.index(" ")]  # 單詞是第一個(gè)空格之前的所有字符
        return str.__new__(cls, word)

    def __gt__(self, other):
        return len(self) > len(other)
    def __lt__(self, other):
        return len(self) < len(other)
    def __ge__(self, other):
        return len(self) >= len(other)
    def __le__(self, other):
        return len(self) <= len(other)

print "foo < fool:", Word("foo") < Word("fool")  # True
print "foolish > fool:", Word("foolish") > Word("fool")  # True
print "bar >= foo:", Word("bar") >= Word("foo")  # True
print "bar <= foo:", Word("bar") <= Word("foo")  # True
print "bar == foo:", Word("bar") == Word("foo")  # False, 用了str內(nèi)置的比較方法來(lái)進(jìn)行比較
print "bar != foo:", Word("bar") != Word("foo")  # True
一元運(yùn)算符和函數(shù)

__pos__(self)

實(shí)現(xiàn)了"+"號(hào)一元運(yùn)算符(比如+some_object)

__neg__(self)

實(shí)現(xiàn)了"-"號(hào)一元運(yùn)算符(比如-some_object)

__invert__(self)

實(shí)現(xiàn)了~號(hào)(波浪號(hào))一元運(yùn)算符(比如~some_object)

__abs__(self)

實(shí)現(xiàn)了abs()內(nèi)建函數(shù).

__round__(self, n)

實(shí)現(xiàn)了round()內(nèi)建函數(shù). 參數(shù)n表示四舍五進(jìn)的精度.

__floor__(self)

實(shí)現(xiàn)了math.floor(), 向下取整.

__ceil__(self)

實(shí)現(xiàn)了math.ceil(), 向上取整.

__trunc__(self)

實(shí)現(xiàn)了math.trunc(), 向0取整.

算術(shù)運(yùn)算符

__add__(self, other)

實(shí)現(xiàn)了加號(hào)運(yùn)算.

__sub__(self, other)

實(shí)現(xiàn)了減號(hào)運(yùn)算.

__mul__(self, other)

實(shí)現(xiàn)了乘法運(yùn)算.

__floordiv__(self, other)

實(shí)現(xiàn)了//運(yùn)算符.

__div__(self, other)

實(shí)現(xiàn)了/運(yùn)算符. 該方法在Python3中廢棄. 原因是Python3中,division默認(rèn)就是true division.

__truediv__(self, other)

實(shí)現(xiàn)了true division. 只有你聲明了from __future__ import division該方法才會(huì)生效.

__mod__(self, other)

實(shí)現(xiàn)了%運(yùn)算符, 取余運(yùn)算.

__divmod__(self, other)

實(shí)現(xiàn)了divmod()內(nèi)建函數(shù).

__pow__(self, other)

實(shí)現(xiàn)了**操作. N次方操作.

__lshift__(self, other)

實(shí)現(xiàn)了位操作<<.

__rshift__(self, other)

實(shí)現(xiàn)了位操作>>.

__and__(self, other)

實(shí)現(xiàn)了位操作&.

__or__(self, other)

實(shí)現(xiàn)了位操作|

__xor__(self, other)

實(shí)現(xiàn)了位操作^

反算術(shù)運(yùn)算符

這里只需要解釋一下概念即可。
假設(shè)針對(duì)some_object這個(gè)對(duì)象:

some_object + other

上面的代碼非常正常地實(shí)現(xiàn)了some_object的__add__方法。那么如果遇到相反的情況呢?

other + some_object

這時(shí)候,如果other沒(méi)有定義__add__方法,但是some_object定義了__radd__, 那么上面的代碼照樣可以運(yùn)行。
這里的__radd__(self, other)就是__add__(self, other)的反算術(shù)運(yùn)算符。

所以,類(lèi)比的,我們就知道了更多的反算術(shù)運(yùn)算符, 就不一一展開(kāi)了:

__rsub__(self, other)

__rmul__(self, other)

__rmul__(self, other)

__rfloordiv__(self, other)

__rdiv__(self, other)

__rtruediv__(self, other)

__rmod__(self, other)

__rdivmod__(self, other)

__rpow__(self, other)

__rlshift__(self, other)

__rrshift__(self, other)

__rand__(self, other)

__ror__(self, other)

__rxor__(self, other)

增量賦值

這也是只要理解了概念就容易掌握的運(yùn)算。舉個(gè)例子:

x = 5
x += 1  # 這里的+=就是增量賦值,將x+1賦值給了x

因此對(duì)于a += b, __iadd__ 將返回a + b, 并賦值給a。
所以很容易理解下面的魔術(shù)方法了:

__iadd__(self, other)

__isub__(self, other)

__imul__(self, other)

__ifloordiv__(self, other)

__idiv__(self, other)

__itruediv__(self, other)

__imod__(self, other)

__ipow__(self, other)

__ilshift__(self, other)

__irshift__(self, other)

__iand__(self, other)

__ior__(self, other)

__ixor__(self, other)

類(lèi)型轉(zhuǎn)化

__int__(self)

實(shí)現(xiàn)了類(lèi)型轉(zhuǎn)化為int的行為.

__long__(self)

實(shí)現(xiàn)了類(lèi)型轉(zhuǎn)化為long的行為.

__float__(self)

實(shí)現(xiàn)了類(lèi)型轉(zhuǎn)化為float的行為.

__complex__(self)

實(shí)現(xiàn)了類(lèi)型轉(zhuǎn)化為complex(復(fù)數(shù), 也即1+2j這樣的虛數(shù))的行為.

__oct__(self)

實(shí)現(xiàn)了類(lèi)型轉(zhuǎn)化為八進(jìn)制數(shù)的行為.

__hex__(self)

實(shí)現(xiàn)了類(lèi)型轉(zhuǎn)化為十六進(jìn)制數(shù)的行為.

__index__(self)

在切片運(yùn)算中將對(duì)象轉(zhuǎn)化為int, 因此該方法的返回值必須是int。用一個(gè)例子來(lái)解釋這個(gè)用法。

class Thing(object):
    def __index__(self):
        return 1

thing = Thing()
list_ = ["a", "b", "c"]
print list_[thing]  # "b"
print list_[thing:thing]  # []

上面例子中, list_[thing]的表現(xiàn)跟list_[1]一致,正是因?yàn)門(mén)hing實(shí)現(xiàn)了__index__方法。

可能有的人會(huì)想,list_[thing]為什么不是相當(dāng)于list_[int(thing)]呢? 通過(guò)實(shí)現(xiàn)Thing的__int__方法能否達(dá)到這個(gè)目的呢?

顯然不能。如果真的是這樣的話(huà),那么list_[1.1:2.2]這樣的寫(xiě)法也應(yīng)該是通過(guò)的。
而實(shí)際上,該寫(xiě)法會(huì)拋出TypeError: slice indices must be integers or None or have an __index__ method

下面我們?cè)僮鰝€(gè)例子,如果對(duì)一個(gè)dict對(duì)象執(zhí)行dict_[thing]會(huì)怎么樣呢?

dict_ = {1: "apple", 2: "banana", 3: "cat"}
print dict_[thing]  # raise KeyError

這個(gè)時(shí)候就不是調(diào)用__index__了。雖然listdict都實(shí)現(xiàn)了__getitem__方法, 但是它們的實(shí)現(xiàn)方式是不一樣的。
如果希望上面例子能夠正常執(zhí)行, 需要實(shí)現(xiàn)Thing的__hash____eq__方法.

class Thing(object):
    def __hash__(self):
        return 1
    def __eq__(self, other):
        return hash(self) == hash(other)

dict_ = {1: "apple", 2: "banana", 3: "cat"}
print dict_[thing]  # apple

__coerce__(self, other)

實(shí)現(xiàn)了混合模式運(yùn)算。

要了解這個(gè)方法,需要先了解coerce()內(nèi)建函數(shù): 官方文檔上的解釋是, coerce(x, y)返回一組數(shù)字類(lèi)型的參數(shù), 它們被轉(zhuǎn)化為同一種類(lèi)型,以便它們可以使用相同的算術(shù)運(yùn)算符進(jìn)行操作。如果過(guò)程中轉(zhuǎn)化失敗,拋出TypeError。

比如對(duì)于coerce(10, 10.1), 因?yàn)?0和10.1在進(jìn)行算術(shù)運(yùn)算時(shí),會(huì)先將10轉(zhuǎn)為10.0再來(lái)運(yùn)算。因此coerce(10, 10.1)返回值是(10.0, 10.1).

__coerce__在Python3中廢棄了。

其他魔術(shù)方法

還沒(méi)講到的魔術(shù)方法還有很多,但有些我覺(jué)得很簡(jiǎn)單,或者很少見(jiàn),就不再累贅展開(kāi)說(shuō)明了。

__str__(self)

對(duì)實(shí)例使用str()時(shí)調(diào)用。

__repr__(self)

對(duì)實(shí)例使用repr()時(shí)調(diào)用。str()repr()都是返回一個(gè)代表該實(shí)例的字符串,
主要區(qū)別在于: str()的返回值要方便人來(lái)看,而repr()的返回值要方便計(jì)算機(jī)看。

__unicode__(self)

對(duì)實(shí)例使用unicode()時(shí)調(diào)用。unicode()str()的區(qū)別在于: 前者返回值是unicode, 后者返回值是str。unicode和str都是basestring的子類(lèi)。

當(dāng)你對(duì)一個(gè)類(lèi)只定義了__str__但沒(méi)定義__unicode__時(shí),__unicode__會(huì)根據(jù)__str__的返回值自動(dòng)實(shí)現(xiàn),即return unicode(self.__str__());
但返回來(lái)則不成立。

class StrDemo2:
    def __str__(self):
        return "StrDemo2"

class StrDemo3:
    def __unicode__(self):
        return u"StrDemo3"

demo2 = StrDemo2()
print str(demo2)  # StrDemo2
print unicode(demo2)  # StrDemo2

demo3 = StrDemo3()
print str(demo3)  # <__main__.StrDemo3 instance>
print unicode(demo3)  # StrDemo3

__format__(self, formatstr)

"Hello, {0:abc}".format(a)等價(jià)于format(a, "abc"), 等價(jià)于a.__format__("abc")

這在需要格式化展示對(duì)象的時(shí)候非常有用,比如格式化時(shí)間對(duì)象。

__hash__(self)

對(duì)實(shí)例使用hash()時(shí)調(diào)用, 返回值是數(shù)值類(lèi)型。

__nonzero__(self)

對(duì)實(shí)例使用bool()時(shí)調(diào)用, 返回True或者False。
你可能會(huì)問(wèn), 為什么不是命名為__bool__? 我也不知道。
我只知道該方法在Python3中改名為__bool__了。

__dir__(self)

對(duì)實(shí)例使用dir()時(shí)調(diào)用。通常實(shí)現(xiàn)該方法是沒(méi)必要的。

__sizeof__(self)

對(duì)實(shí)例使用sys.getsizeof()時(shí)調(diào)用。返回對(duì)象的大小,單位是bytes。

__instancecheck__(self, instance)

對(duì)實(shí)例調(diào)用isinstance(instance, class)時(shí)調(diào)用。 返回值是布爾值。它會(huì)判斷instance是否是該類(lèi)的實(shí)例。

__subclasscheck__(self, subclass)

對(duì)實(shí)例使用issubclass(subclass, class)時(shí)調(diào)用。返回值是布爾值。它會(huì)判斷subclass否是該類(lèi)的子類(lèi)。

__copy__(self)

對(duì)實(shí)例使用copy.copy()時(shí)調(diào)用。返回"淺復(fù)制"的對(duì)象。

__deepcopy__(self, memodict={})

對(duì)實(shí)例使用copy.deepcopy()時(shí)調(diào)用。返回"深復(fù)制"的對(duì)象。

__call__(self, [args...])

該方法允許類(lèi)的實(shí)例跟函數(shù)一樣表現(xiàn):

class XClass:
    def __call__(self, a, b):
        return a + b

def add(a, b):
    return a + b

x = XClass()
print "x(1, 2)", x(1, 2)
print "callable(x)", callable(x)  # True
print "add(1, 2)", add(1, 2)
print "callable(add)", callable(add)  # True
Python3中的差異

Python3中,str與unicode的區(qū)別被廢除了,因而__unicode__沒(méi)有了,取而代之地出現(xiàn)了__bytes__.

Python3中,division默認(rèn)就是true division, 因而__div__廢棄.

__coerce__因存在冗余而廢棄.

__cmp__因存在冗余而廢棄.

__nonzero__改名為__bool__.

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/44214.html

相關(guān)文章

  • 在TypeScript項(xiàng)目中像PHP一樣使用魔術(shù)變量

    摘要:當(dāng)中有許多很有用的魔術(shù)變量比如之類(lèi)但是中并沒(méi)有因此我寫(xiě)了一個(gè)插件來(lái)使用它們?cè)创a已經(jīng)放到了上使用方法首先你需要安裝這個(gè)包修改一下你的在代碼中使用魔術(shù)變量比如用來(lái)編譯你的項(xiàng)目注意這里不能用因?yàn)闆](méi)有開(kāi)放接口需要全局安裝一 PHP 當(dāng)中有許多很有用的魔術(shù)變量, 比如__CLASS__, __METHOD__之類(lèi). 但是typescript中并沒(méi)有. 因此我寫(xiě)了一個(gè)插件typescript-m...

    ?xiaoxiao, 評(píng)論0 收藏0
  • 搞定PHP面試 - PHP魔術(shù)方法知識(shí)點(diǎn)整理

    摘要:魔術(shù)方法知識(shí)點(diǎn)整理代碼使用語(yǔ)法編寫(xiě)一構(gòu)造函數(shù)和析構(gòu)函數(shù)構(gòu)造函數(shù)具有構(gòu)造函數(shù)的類(lèi)會(huì)在每次創(chuàng)建新對(duì)象時(shí)先調(diào)用此方法,所以非常適合在使用對(duì)象之前做一些初始化工作。在析構(gòu)函數(shù)中調(diào)用將會(huì)中止其余關(guān)閉操作的運(yùn)行。析構(gòu)函數(shù)中拋異常會(huì)導(dǎo)致致命錯(cuò)誤。 PHP魔術(shù)方法知識(shí)點(diǎn)整理 代碼使用PHP7.2語(yǔ)法編寫(xiě) 一、構(gòu)造函數(shù)和析構(gòu)函數(shù) __construct() 構(gòu)造函數(shù) __construct ([ mi...

    付永剛 評(píng)論0 收藏0
  • php magic method 具體應(yīng)用和 phpdoc 結(jié)合

    摘要:關(guān)于的介紹自行查閱官方文檔,這里不再贅述。使用的同學(xué)注意了,如果在我們的代碼中使用到了中相關(guān)的魔術(shù)方法,需要在文件中指明告訴應(yīng)該如何來(lái)跟蹤變量屬性。下面我們來(lái)具體實(shí)踐分析。確實(shí)這個(gè)樣子可以實(shí)現(xiàn),但沒(méi)有利用到這一魔術(shù)方法的特性。 關(guān)于 Magic Methods 的介紹自行查閱官方文檔,這里不再贅述。http://php.net/manual/en/lang... 使用 phpstorm...

    csRyan 評(píng)論0 收藏0
  • 使用compass自動(dòng)合并css雪碧圖(css sprite)

    摘要:使用雪碧圖,能夠減少頁(yè)面的請(qǐng)求數(shù)降低圖片占用的字節(jié),以此來(lái)達(dá)到提升頁(yè)面訪(fǎng)問(wèn)速度的目的。也正是因?yàn)檫@一點(diǎn),導(dǎo)致很多開(kāi)發(fā)者懶于使用雪碧圖。本文就介紹下怎樣使用來(lái)自動(dòng)合并雪碧圖。生成的每個(gè)雪碧圖默認(rèn)的規(guī)則是目錄名圖片名。 css雪碧圖又叫css精靈或css sprite,是一種背景圖片的拼合技術(shù)。使用css雪碧圖,能夠減少頁(yè)面的請(qǐng)求數(shù)、降低圖片占用的字節(jié),以此來(lái)達(dá)到提升頁(yè)面訪(fǎng)問(wèn)速度的目的。但...

    劉永祥 評(píng)論0 收藏0
  • Python 魔術(shù)方法做出更好正則表達(dá)式 API

    摘要:注原文地址為我的一個(gè)同事提到他錯(cuò)過(guò)了的正則表達(dá)式的語(yǔ)法糖。首先,從正則表達(dá)式檢索捕捉組需要兩個(gè)步驟。語(yǔ)法糖為了好玩,我把一個(gè)小小的增加了一些語(yǔ)法糖的正則表達(dá)式庫(kù)的幫助類(lèi)放在一起。調(diào)用將調(diào)用類(lèi)的方法。 注:原文地址為 Playing with Python Magic Methods to make a nicer Regex API 我的一個(gè)同事提到,他錯(cuò)過(guò)了 Ruby 的正...

    MangoGoing 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<