一. item函數(shù)
item
函數(shù)可以把對(duì)象操作模擬成字典操作, item
函數(shù)包括以下三個(gè)函數(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
-
__slots__
是什么:是一個(gè)類變量,變量值可以是列表,元組,或者其他可迭代對(duì)象,也可以是一個(gè)字符串(意味著所有實(shí)例只有一個(gè)數(shù)據(jù)屬性) - 當(dāng)一個(gè)類有較少的屬性尺棋,但是會(huì)多次實(shí)例化時(shí),為了節(jié)省內(nèi)存可以使用
__slots__
取代實(shí)例的__dict__
當(dāng)你定義__slots__
后。類的實(shí)例通過一個(gè)很小的固定大小的數(shù)組來構(gòu)建,而不是為每個(gè)實(shí)例定義一個(gè)
字典,這跟元組或列表很類似绵跷。在__slots__
中列出的屬性名在內(nèi)部被映射到這個(gè)數(shù)組的指定小標(biāo)上膘螟。使用__slots__
的一個(gè)缺點(diǎn)就是我們不能給實(shí)例添加新的屬性,只能使用在__slots__
中定義的那些屬性名。 - 注意事項(xiàng):
__slots__
的很多特性都依賴于普通的基于字典的實(shí)現(xiàn)碾局。另外,定義了__slots__
后的類不再 支持一些普通類特性了,比如多繼承荆残。大多數(shù)情況下,應(yīng)該只在那些經(jīng)常被使用到 的用作數(shù)據(jù)結(jié)構(gòu)的類上定義__slots__
。
__slots__
可以作為一個(gè)封裝工具來防止用戶給實(shí)例增加新的屬性净当。使用__slots__
可以達(dá)到這樣的目的,但是__slots__
的設(shè)計(jì)初衷是一個(gè)內(nèi)存優(yōu)化工具内斯。
class Foo:
__slots__='x'
f1=Foo()
f1.x=1
f1.y=2#報(bào)錯(cuò)
print(f1.__slots__) #使用__slots__后,不再有__dict__
class Bar:
__slots__=['x','y']
n=Bar()
n.x,n.y=1,2
n.z=3#報(bào)錯(cuò)
三. 迭代器協(xié)議
迭代器協(xié)議: 對(duì)象必須提供一個(gè)next方法蚯瞧,執(zhí)行該方法要么返回迭代中的下一項(xiàng)嘿期,要么就引起一個(gè)stopIteration的異常終止迭代
遵循迭代器協(xié)議使用__iter__
和__next__
函數(shù)實(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)
四. 上下文管理
操作文件的時(shí)候可以使用with
語句,with
語句遵循上下文協(xié)議埋合。使用__enter__
和__exit__
方法可以讓一個(gè)對(duì)象支持上下文協(xié)議备徐。
使用with
語句可以實(shí)現(xiàn)with中的語句執(zhí)行結(jié)束后自動(dòng)完成清理工作
一般在文件操作,網(wǎng)絡(luò)連接和鎖的編程環(huán)境中甚颂,在__exit__
中設(shè)計(jì)自動(dòng)釋放資源的機(jī)制蜜猾,實(shí)現(xiàn)自動(dòng)釋放資源
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)建對(duì)象觸發(fā)的蹭睡,即:對(duì)象 = 類名() ;
對(duì)于 __call__
方法的執(zhí)行是由對(duì)象后加括號(hào)觸發(fā)的赶么,即:對(duì)象() 或者 類()()
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)建對(duì)象的模板一樣
元類的實(shí)例為類辫呻,正如類的實(shí)例為對(duì)象
type是python的一個(gè)內(nèi)建元類清钥,用來直接控制生成類,python中任何class定義的類其實(shí)都是type類實(shí)例化的對(duì)象
創(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}
一個(gè)類沒有聲明自己的元類放闺,默認(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__
名字加括號(hào)的本質(zhì)(即,任何name()
的形式),都是先找到name的父類,然后執(zhí)行:父類.__call__
而父類.__call__
一般做兩件事:
調(diào)用父類.__new__
方法并返回一個(gè)對(duì)象
調(diào)用父類.__init__
方法對(duì)子類進(jìn)行初始化
class 定義Foo,并指定元類為Mymeta,這就相當(dāng)于要用Mymeta創(chuàng)建一個(gè)新的對(duì)象Foo,于是相當(dāng)于執(zhí)行
Foo=Mymeta('foo',(...),{...})
因此我們可以看到,只定義class就會(huì)有如下執(zhí)行效果
Mymeta.__new__
Mymeta.__init__
實(shí)際上class Foo(metaclass=Mymeta)是觸發(fā)了Foo=Mymeta('Foo',(...),{...})操作,
遇到了名字加括號(hào)的形式,即Mymeta(...),于是就去找Mymeta的父類type
,然后執(zhí)行type.__call__(...)
方法
于是觸發(fā)Mymeta.__new__
方法得到一個(gè)具體的對(duì)象,然后觸發(fā)Mymeta.__init__
方法對(duì)對(duì)象進(jìn)行初始化
f=Foo('Joe')的原理同上