python黑魔法 -- 內置方法使用

不少pythonic的代碼都會用到內置方法,根據本身的經驗,羅列一下本身知道的內置方法。python

__getitem__ __setitem__ __delitem__

這三個方法是字典類的內置方法,分別對應於查找、設置、刪除操做,以一個簡單的例子說明:閉包

class A(dict):
    def __getitem__(self, key):
        print '__getitem__'
        return super(A, self).__getitem__(key)

    def __setitem__(self, key, value):
        print '__setitem__'
        return super(A, self).__setitem__(key, value)

    def __delitem__(self, key):
        print '__delitem__'
        return super(A, self).__delitem__(key)

a = A()
a[1] = 1
b = a[1]
del a[1]
a.get(1)

上面的代碼中a[1] = 1實際上調用的就是a.__setitem__(1, 1), a[1]調用的是a.__get__item__(1), del a[1]調用的是a.__setitem__(1)。須要注意的是,字典示例的get方法和__getitem__方法不存在調用關係,二者互不影響。函數

__getattribute__ __getattr__ __setattr__ __delattr__

__getattribute__和__getattr__都是從python對象示例中獲取成員變量的方法,差異在於__getattribute__在任什麼時候候都會調用,而__getattr__只有在__getattribute__執行完成以後而且沒有找到成員變量的時候纔會執行。__setattr__在給成員變量賦值的時候調用,__delattr__在回收成員變量的時候調用,一下面的例子說明:code

class A(object):
    x = []

    def __getattribute__(self, name):
        print '__getattribute__'
        return super(A, self).__getattribute__(name)

    def __setattr__(self, key, value):
        print '__setattr__'
        return super(A, self).__setattr__(key, value)

    def __getattr__(self, item):
        print '__getattr__'

    def __delattr__(self, item):
        print '__delattr__'
        return super(A, self).__delattr__(item)

a = A()
a.x
a.y
b = getattr(a, 'x')
b = getattr(a, 'y')
a.x = 1
a.y = 1
setattr(a, 'x', 1)
setattr(a, 'y', 1)
del a.x
del a.y

由於x是a的成員變量,a.x會調用a.__getattribue__('x'),而y不是a的成員變量,在調用a.__getattribue__('y')以後還會調用a.__getattr__('y'),內置方法getattr也是按照此順序調用,惟一的區別在於getattr在成員變量不存在的時候不會拋出異常,而是給一個默認值。a.x = 1setattr(a, 'x', 1)都會調用a.__setattr__('x', 1),若是原來的成員變量不存在,__setattr__會給實例增長一個成員變量,而不是拋出異常。對象

__call__

若是重載了__call__方法,則實例對象就能夠像方法同樣調用了。若是實例a的類實現了這個方法,那麼a(*args, **kwargs)就會調用a.__call__(*args, **kwargs)。用這種方法能夠簡單方便的實現裝飾器,以下所示:get

class A(object):
    def __init__(self, func):
        self.func = func

    def __call__(self, *args, **kwargs):
        print '__call__'
        return self.func(*args, **kwargs)

@A
def foo(x):
    return x

foo(1)

裝飾器語法糖至關於foo = A(foo), 和閉包不一樣,foo已經不是一個函數而是類A的實例。foo(1)會執行foo.__call__(1)。固然這個例子實現的裝飾器並很差,會改變foo的函數簽名,並且也不能裝飾類方法。it

此外元類的__call__也能夠控制實例的建立過程,由於當類建立對象時,元類的__call__函數就被調用,進而調用type.__call__建立對象,type.__call__回依次調用類的__new__和__init__生成實例。以單例模式爲例:class

class Singleton(type):
    def __call__(cls, *args):
        print "Singleton call"
        if not hasattr(cls, 'instance'):
            cls.instance = super(Singleton, cls).__call__(*args)
        return cls.instance

class Cache(object):

    __metaclass__ = Singleton

    def __new__(cls, *args):
        print "Cache new"
        return object.__new__(cls, *args)

    def __init__(cls, *args, **kwargs):
        print "Cache init"

a = Cache()
b = Cache()
print a is b

__get__ __set__

這三個方法分別對應於描述器的查詢、設置、刪除函數,仍是以一個簡單的例子來講明:import

class A(object):

    def __get__(self, instance, owner):
        print '__get__'

    def __set__(self, instance, value):
        print '__set__'


class B(object):
    a = A()

b = B()
c = b.a
b.a = 1

類A的示例a做爲b的成員變量時候,訪問示例b中成員變量a的時候會調用a.__get__(b, B),修改b中成員變量a的時候會調用a.__set__(b, 1)。須要注意的是,當用B.a訪問的時候參數instance爲None。實際上像classmethod,staticmethod這樣的裝飾器都是經過裝飾器來實現的,經過裝飾器就能夠解決上面不能做爲類方法裝飾器的問題。變量

import types

class Profiled(object):
    def __init__(self, func):
        self.func = func

    def __call__(self, *args, **kwargs):
        return self.func(*args, **kwargs)

    def __get__(self, instance, owner):
        if instance is None:
            return self
        else:
            return types.MethodType(self, instance)


class Spam(object):
    @Profiled
    def bar(self, x):
        return x + 1

s = Spam()
print s.bar(5)

在__get__方法中,不是Spam.bar調用,則instance不爲None的狀況下,會用types.MethodType把bar和s作一個綁定,s.bar(5)等價於執行了bar.__get__(s, Spam).__call__(5),看起來很繞,但實際上用起來很方便。

相關文章
相關標籤/搜索
本站公眾號
   歡迎關注本站公眾號,獲取更多信息