item函數(shù),slots,迭代器協(xié)議,上下文管理,元類

一. item函數(shù)

item函數(shù)可以把對象操作模擬成字典操作, item函數(shù)包括以下三個函數(shù)__getitem____setitem____delitem__
具體用法如下所示

class Foo:
    def __init__(self, name):
        self.name = name

    def __getitem__(self, item):
        return self.__dict__[item]

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

    def __delitem__(self, key):
        self.__dict__.pop(key)

f = Foo('j')
print(f['name'])
f['gender'] = 'male'
print(f.__dict__)

del f['gender']
print(f.__dict__)

>>j
>>{'name': 'j', 'gender': 'male'}
>>{'name': 'j'}

二. slots

  1. __slots__是什么:是一個類變量,變量值可以是列表,元組,或者其他可迭代對象,也可以是一個字符串(意味著所有實例只有一個數(shù)據(jù)屬性)
  2. 當(dāng)一個類有較少的屬性,但是會多次實例化時,為了節(jié)省內(nèi)存可以使用__slots__取代實例的__dict__
    當(dāng)你定義__slots__后。類的實例通過一個很小的固定大小的數(shù)組來構(gòu)建,而不是為每個實例定義一個
    字典,這跟元組或列表很類似。在__slots__中列出的屬性名在內(nèi)部被映射到這個數(shù)組的指定小標(biāo)上。使用__slots__的一個缺點就是我們不能給實例添加新的屬性,只能使用在__slots__中定義的那些屬性名。
  3. 注意事項:__slots__的很多特性都依賴于普通的基于字典的實現(xiàn)。另外,定義了__slots__后的類不再 支持一些普通類特性了,比如多繼承。大多數(shù)情況下,應(yīng)該只在那些經(jīng)常被使用到 的用作數(shù)據(jù)結(jié)構(gòu)的類上定義__slots__
    __slots__可以作為一個封裝工具來防止用戶給實例增加新的屬性。使用__slots__可以達(dá)到這樣的目的,但是__slots__的設(shè)計初衷是一個內(nèi)存優(yōu)化工具。
class Foo:
    __slots__='x'

f1=Foo()
f1.x=1
f1.y=2#報錯
print(f1.__slots__) #使用__slots__后,不再有__dict__

class Bar:
    __slots__=['x','y']
    
n=Bar()
n.x,n.y=1,2
n.z=3#報錯

三. 迭代器協(xié)議

迭代器協(xié)議: 對象必須提供一個next方法,執(zhí)行該方法要么返回迭代中的下一項,要么就引起一個stopIteration的異常終止迭代
遵循迭代器協(xié)議使用__iter____next__函數(shù)實現(xiàn)range函數(shù)

class Range:
    def __init__(self, value, start=0, step=1):
        self.value = value
        self.start = start
        self.step = step

    def __iter__(self):    # 使用__iter__函數(shù)為Range類增加可迭代屬性
        return self

    def __next__(self):   # 使用__next__方法得到迭代返回值
        if self.start >= self.value:
            raise StopIteration
        n = self.start
        self.start += self.step
        return n

for i in Range(3):
    print(i)

四. 上下文管理

操作文件的時候可以使用with語句,with語句遵循上下文協(xié)議。使用__enter____exit__方法可以讓一個對象支持上下文協(xié)議。
使用with語句可以實現(xiàn)with中的語句執(zhí)行結(jié)束后自動完成清理工作
一般在文件操作,網(wǎng)絡(luò)連接和鎖的編程環(huán)境中,在__exit__中設(shè)計自動釋放資源的機制,實現(xiàn)自動釋放資源

class Foo:
    def __init__(self, filepath, mod='r', encode='utf-8'):
        self.f = open(filepath, mod, encoding=encode)
        self.filepath = filepath
        self.mod = mod

    def write(self, line):
        self.f.write(line)

    def __getattr__(self, item):
        return getattr(self.f, item)

    def __enter__(self):
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        self.f.close()
        del self.f
        return True   # 返回True可以吞掉異常,異常發(fā)生后,`with`代碼塊外的語句還可以繼續(xù)執(zhí)行

    def __del__(self):
        print('close file')

with Foo('a.txt', 'w') as f:
    f.write('ddddd')
    print('------>')

五. 元類

__call__

構(gòu)造方法的執(zhí)行是由創(chuàng)建對象觸發(fā)的,即:對象 = 類名() ;
對于 __call__方法的執(zhí)行是由對象后加括號觸發(fā)的,即:對象() 或者 類()()

class Foo:
    def __init__(self):
        print('__init__')
        pass

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


obj = Foo()  # 執(zhí)行 __init__
obj()  # 執(zhí)行 __call__

>>__init__
>>__call__

metaclass

元類是類的類,是類的模板
元類是用來控制如何創(chuàng)建類的,正如類是創(chuàng)建對象的模板一樣
元類的實例為類,正如類的實例為對象
type是python的一個內(nèi)建元類,用來直接控制生成類,python中任何class定義的類其實都是type類實例化的對象
創(chuàng)建類的兩種方法

# 方法一
class Foo_1:
    x = 1
    def func(self):
        print('func')
print(Foo_1.__dict__)

#{'__module__': '__main__', 
# 'x': 1, 
# 'func': <function Foo_1.func at 0x000002018A47EAE8>, 
# '__dict__': <attribute '__dict__' of 'Foo_1' objects>, 
# '__weakref__': <attribute '__weakref__' of 'Foo_1' objects>, 
# '__doc__': None}
#{'__module__': '__main__',

# 方法二
def func(self):
    print('func')
x = 1
Foo_2 = type('Foo_2', (object, ), {'func': func, 'x': 1})
print(Foo_2.__dict__)

#{'__module__': '__main__',
# 'x': 1,
# 'func': <function func at 0x0000020189FB3E18>, 
# '__dict__': <attribute '__dict__' of 'Foo_2' objects>, 
# '__weakref__': <attribute '__weakref__' of 'Foo_2' objects>, 
# '__doc__': None}

一個類沒有聲明自己的元類,默認(rèn)他的元類就是type,除了使用元類type,用戶也可以通過繼承type來自定義元類

class Mymeta(type):
    def __init__(self,name,bases,dic):
        print('Mymeta.__init__')

    def __new__(cls, *args, **kwargs):
        print('Mymeta.__new__')
        return type.__new__(cls,*args,**kwargs)

    def __call__(self, *args, **kwargs):
        print('Mymeta.__call__')
        obj=self.__new__(self)
        self.__init__(self,*args,**kwargs)
        return obj

class Foo(object,metaclass=Mymeta):
    def __init__(self,name):
        print('Foo.__init__')
        self.name=name
    def __new__(cls, *args, **kwargs):
        print('Foo.__new__')
        return object.__new__(cls)

f = Foo('Joe')

>>Mymeta.__new__
>>Mymeta.__init__
>>Mymeta.__call__
>>Foo.__new__
>>Foo.__init__

名字加括號的本質(zhì)(即,任何name()的形式),都是先找到name的父類,然后執(zhí)行:父類.__call__

父類.__call__一般做兩件事:
調(diào)用父類.__new__方法并返回一個對象
調(diào)用父類.__init__方法對子類進行初始化

class 定義Foo,并指定元類為Mymeta,這就相當(dāng)于要用Mymeta創(chuàng)建一個新的對象Foo,于是相當(dāng)于執(zhí)行
Foo=Mymeta('foo',(...),{...})
因此我們可以看到,只定義class就會有如下執(zhí)行效果

Mymeta.__new__
Mymeta.__init__

實際上class Foo(metaclass=Mymeta)是觸發(fā)了Foo=Mymeta('Foo',(...),{...})操作,
遇到了名字加括號的形式,即Mymeta(...),于是就去找Mymeta的父類type,然后執(zhí)行type.__call__(...)方法
于是觸發(fā)Mymeta.__new__方法得到一個具體的對象,然后觸發(fā)Mymeta.__init__方法對對象進行初始化

f=Foo('Joe')的原理同上

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

推薦閱讀更多精彩內(nèi)容

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 134,915評論 18 139
  • 1. Java基礎(chǔ)部分 基礎(chǔ)部分的順序:基本語法,類相關(guān)的語法,內(nèi)部類的語法,繼承相關(guān)的語法,異常的語法,線程的語...
    子非魚_t_閱讀 31,765評論 18 399
  • 1.元類 1.1.1類也是對象 在大多數(shù)編程語言中,類就是一組用來描述如何生成一個對象的代碼段。在Python中這...
    TENG書閱讀 1,306評論 0 3
  • 以前總是聽哈佛大學(xué)凌晨3點4點還有學(xué)子在圖書館奮斗在一線,現(xiàn)在也總算是能夠體會了,當(dāng)你特別想要去做一件事情...
    熙熙Breathe閱讀 393評論 0 6
  • 作者:Yvonne ?? 編輯:阿拉 《指數(shù)型組織》: 有人問,學(xué)那么多有什么用?從指數(shù)型思維看,量的累積初期你看...
    福州十點讀書會閱讀 316評論 1 0