一、私有屬性
-
我們大家知道在類(lèi)里面定義的屬性名前加兩個(gè)下劃線就是私有屬性,它是不能在外面被訪問(wèn)的,如下:
class Person(object): def __init__(self,name): self.__name = name person = Person("小王") print(person.__name)打印結(jié)果:報(bào)錯(cuò),
__name是私有的,外面是無(wú)法訪問(wèn)的AttributeError: 'Person' object has no attribute '__name'分析上面報(bào)錯(cuò)的原因是:
__name被Python改了名字,一種假象而已,我們可以看看Python把它改成了什么,我們通過(guò)魔法屬性person.__dict__來(lái)打印print(person.__dict__)打印結(jié)果是:
{'_Person__name': '小王'}看到上面的打印,其實(shí)Python是把私有的屬性改成了 一個(gè)下劃線+類(lèi)名+私有屬性 :
_類(lèi)名私有屬性,如上面的__name->_Person__name,那么我們就可以通過(guò)實(shí)例對(duì)象.被改后的私有屬性名來(lái)打印私有屬性,如下print(person._Person__name)打印結(jié)果是:
小王
二、魔法屬性
無(wú)論人或事物往往都有不按套路出牌的情況,Python的類(lèi)屬性也是如此,存在著一些具有特殊含義的屬性,詳情如下:
-
2.1、
__doc__: 表示類(lèi)的描述信息class Foo: """ 描述類(lèi)信息,這是用于看片的神奇 """ def func(self): pass print(Foo.__doc__) #輸出:類(lèi)的描述信息 -
2.2、
__module__和__class____module__表示當(dāng)前操作的對(duì)象在那個(gè)模塊__class__表示當(dāng)前操作的對(duì)象的類(lèi)是什么,也就是打印類(lèi)對(duì)象的名字-
test.py
class Person(object): def __init__(self): self.name = 'laowang' -
main.py
from test import Person obj = Person() print(obj.__module__) # 輸出 test 即:輸出模塊 print(obj.__class__) # 輸出 test.Person 即:輸出類(lèi)
-
2.3、
__init__: 初始化方法,通過(guò)類(lèi)創(chuàng)建對(duì)象時(shí),自動(dòng)觸發(fā)執(zhí)行class Person: def __init__(self, name): self.name = name self.age = 18 obj = Person('laowang') # 自動(dòng)執(zhí)行類(lèi)中的 __init__ 方法 -
2.4、
__del__:當(dāng)對(duì)象在內(nèi)存中被釋放時(shí),自動(dòng)觸發(fā)執(zhí)行。
提示:此方法一般無(wú)須定義,因?yàn)镻ython是一門(mén)高級(jí)語(yǔ)言,程序員在使用時(shí)無(wú)需關(guān)心內(nèi)存的分配和釋放,因?yàn)榇斯ぷ鞫际墙唤oPython解釋器來(lái)執(zhí)行,所以,__del__的調(diào)用是由解釋器在進(jìn)行垃圾回收時(shí)自動(dòng)觸發(fā)執(zhí)行的。class Foo: def __del__(self): pass -
2.5、
__call__: 對(duì)象后面加括號(hào),觸發(fā)執(zhí)行
提示:__init__方法的執(zhí)行是由創(chuàng)建對(duì)象觸發(fā)的,即:對(duì)象 = 類(lèi)名();而對(duì)于__call__方法的執(zhí)行是由對(duì)象后加括號(hào)觸發(fā)的,即:對(duì)象()或者類(lèi)()()class Foo: def __init__(self): pass def __call__(self, *args, **kwargs): print('__call__') obj = Foo() # 執(zhí)行 __init__ obj() # 執(zhí)行 __call__ -
2.6、
__dict__: 類(lèi)或?qū)ο笾械乃袑傩?類(lèi)的實(shí)例屬性屬于對(duì)象;類(lèi)中的類(lèi)屬性和方法等屬于類(lèi)class Province(object): country = 'China' def __init__(self, name, count): self.name = name self.count = count def func(self, *args, **kwargs): print('func') # 獲取類(lèi)的屬性,即:類(lèi)屬性、方法、 print(Province.__dict__) # 輸出:{'__dict__': <attribute '__dict__' of 'Province' objects>, '__module__': '__main__', 'country': 'China', '__doc__': None, '__weakref__': <attribute '__weakref__' of 'Province' objects>, 'func': <function Province.func at 0x101897950>, '__init__': <function Province.__init__ at 0x1018978c8>} obj1 = Province('山東', 10000) print(obj1.__dict__) # 獲取 對(duì)象obj1 的屬性 # 輸出:{'count': 10000, 'name': '山東'} obj2 = Province('山西', 20000) print(obj2.__dict__) # 獲取 對(duì)象obj1 的屬性 # 輸出:{'count': 20000, 'name': '山西'} -
2.7、
__str__: 如果一個(gè)類(lèi)中定義了__str__方法,那么在打印 對(duì)象 時(shí),默認(rèn)輸出該方法的返回值class Foo: def __str__(self): return '小李' obj = Foo() print(obj) # 輸出:小李 -
2.8、
__getitem__、__setitem__、__delitem__: 用于索引操作,如字典。以上分別表示 獲取、設(shè)置、刪除 數(shù)據(jù)class Foo(object): def __getitem__(self, key): print('__getitem__', key) def __setitem__(self, key, value): print('__setitem__', key, value) def __delitem__(self, key): print('__delitem__', key) obj = Foo() result = obj['k1'] # 自動(dòng)觸發(fā)執(zhí)行 __getitem__ obj['k2'] = 'laowang' # 自動(dòng)觸發(fā)執(zhí)行 __setitem__ del obj['k1'] # 自動(dòng)觸發(fā)執(zhí)行 __delitem__ -
2.9、
__getslice__、__setslice__、__delslice__: 該三個(gè)方法用于分片操作,如:列表class Foo(object): def __getslice__(self, i, j): print('__getslice__', i, j) def __setslice__(self, i, j, sequence): print('__setslice__', i, j) def __delslice__(self, i, j): print('__delslice__', i, j) obj = Foo() obj[-1:1] # 自動(dòng)觸發(fā)執(zhí)行 __getslice__ obj[0:1] = [11,22,33,44] # 自動(dòng)觸發(fā)執(zhí)行 __setslice__ del obj[0:2] # 自動(dòng)觸發(fā)執(zhí)行 __delslice__
三、with與上下文管理器”
-
3.1、對(duì)于 系統(tǒng)資源如文件、數(shù)據(jù)庫(kù)連接、socket 而言,應(yīng)用程序打開(kāi)這些資源并執(zhí)行完業(yè)務(wù)邏輯之后,必須做的一件事就是要關(guān)閉(斷開(kāi))該資源。
比如 Python 程序打開(kāi)一個(gè)文件,往文件中寫(xiě)內(nèi)容,寫(xiě)完之后,就要關(guān)閉該文件,否則會(huì)出現(xiàn)什么情況呢?極端情況下會(huì)出現(xiàn) "Too many open files" 的錯(cuò)誤,因?yàn)橄到y(tǒng)允許你打開(kāi)的最大文件數(shù)量是有限的。同樣,對(duì)于數(shù)據(jù)庫(kù),如果連接數(shù)過(guò)多而沒(méi)有及時(shí)關(guān)閉的話,就可能會(huì)出現(xiàn) "Can not connect to MySQL server Too many connections",因?yàn)閿?shù)據(jù)庫(kù)連接是一種非常昂貴的資源,不可能無(wú)限制的被創(chuàng)建。
-
3.2、看看如何正確關(guān)閉一個(gè)文件。
-
普通版:打開(kāi)文件之后直接進(jìn)行讀寫(xiě)操作
def m1(): f = open("output.txt", "w") f.write("python之禪") f.close()這樣寫(xiě)有一個(gè)潛在的問(wèn)題,如果在調(diào)用 write 的過(guò)程中,出現(xiàn)了異常進(jìn)而導(dǎo)致后續(xù)代碼無(wú)法繼續(xù)執(zhí)行,close 方法無(wú)法被正常調(diào)用,因此資源就會(huì)一直被該程序占用者釋放。那么該如何改進(jìn)代碼呢?
-
進(jìn)階版: 看著不夠簡(jiǎn)潔哈,后面還有高級(jí)版
def m2(): f = open("output.txt", "w") try: f.write("python之禪") except IOError: print("oops error") finally: f.close()改良版本的程序是對(duì)可能發(fā)生異常的代碼處進(jìn)行 try 捕獲,使用
try/finally語(yǔ)句,該語(yǔ)句表示如果在 try 代碼塊中程序出現(xiàn)了異常,后續(xù)代碼就不再執(zhí)行,而直接跳轉(zhuǎn)到 except 代碼塊。而無(wú)論如何,finally 塊的代碼最終都會(huì)被執(zhí)行。因此,只要把 close 放在 finally 代碼中,文件就一定會(huì)關(guān)閉。 -
高級(jí)版: 這么看起來(lái)就非常爽了
def m3(): with open("output.txt", "r") as f: f.write("Python之禪")一種更加簡(jiǎn)潔、優(yōu)雅的方式就是用 with 關(guān)鍵字。open 方法的返回值賦值給變量 f,當(dāng)離開(kāi) with 代碼塊的時(shí)候,系統(tǒng)會(huì)自動(dòng)調(diào)用 f.close() 方法, with 的作用和使用 try/finally 語(yǔ)句是一樣的。那么它的實(shí)現(xiàn)原理是什么?在講 with 的原理前要涉及到另外一個(gè)概念,就是上下文管理器(Context Manager)。
-
-
3.3、上下文管理器(Context Manager)
-
什么是上下文(context)?
- 看,一篇文章,給你摘錄一段,沒(méi)前沒(méi)后,你讀不懂,因?yàn)橛姓Z(yǔ)境,就是語(yǔ)言環(huán)境存在,一段話說(shuō)了什么,要通過(guò)上下文(文章的上下文)來(lái)推斷。
- app點(diǎn)擊一個(gè)按鈕進(jìn)入一個(gè)新的界面,也要保存你是在哪個(gè)屏幕跳過(guò)來(lái)的等等信息,以便你點(diǎn)擊返回的時(shí)候能正確跳回,如果不存肯定就無(wú)法正確跳回了。
- 看這些都是上下文的典型例子,理解成環(huán)境就可以,(而且上下文雖然叫上下文,但是程序里面一般都只有上文而已,只是叫的好聽(tīng)叫上下文。。進(jìn)程中斷在操作系統(tǒng)中是有上有下的,不過(guò)不這個(gè)高深的問(wèn)題就不要深究了。。。)
-
上下文管理器
任何實(shí)現(xiàn)了__enter__()和__exit__()方法的對(duì)象都可稱(chēng)之為上下文管理器,上下文管理器對(duì)象可以使用with關(guān)鍵字。顯然,文件(file)對(duì)象也實(shí)現(xiàn)了上下文管理器。
那么文件對(duì)象是如何實(shí)現(xiàn)這兩個(gè)方法的呢?我們可以模擬實(shí)現(xiàn)一個(gè)自己的文件類(lèi),讓該類(lèi)實(shí)現(xiàn)__enter__()和__exit__()方法。class File(): def __init__(self, filename, mode): self.filename = filename self.mode = mode def __enter__(self): print("entering") self.f = open(self.filename, self.mode) return self.f def __exit__(self, *args): print("will exit") self.f.close()__enter__()方法返回資源對(duì)象,這里就是你將要打開(kāi)的那個(gè)文件對(duì)象,__exit__()方法處理一些清除工作。
因?yàn)?File 類(lèi)實(shí)現(xiàn)了上下文管理器,現(xiàn)在就可以使用 with 語(yǔ)句了。with File('out.txt', 'w') as f: print("writing") f.write('hello, python')提示:
File('out.txt', 'w')去執(zhí)行的時(shí)候就會(huì)去調(diào)用里面的_enter__方法返回一個(gè)操作文本的對(duì)象,當(dāng)在進(jìn)行寫(xiě)的操作的時(shí)候,如果出現(xiàn)了異常,會(huì)自動(dòng)調(diào)用__exit__,釋放響應(yīng)的資源,這樣,你就無(wú)需顯示地調(diào)用 close 方法了,由系統(tǒng)自動(dòng)去調(diào)用,哪怕中間遇到異常 close 方法也會(huì)被調(diào)用。
-
-
3.4、實(shí)現(xiàn)上下文管理器的另外方式
Python 還提供了一個(gè)contextmanager的裝飾器,更進(jìn)一步簡(jiǎn)化了上下文管理器的實(shí)現(xiàn)方式。通過(guò) yield 將函數(shù)分割成兩部分,yield 之前的語(yǔ)句在__enter__方法中執(zhí)行,yield 之后的語(yǔ)句在__exit__方法中執(zhí)行。緊跟在yield后面的值是函數(shù)的返回值。from contextlib import contextmanager @contextmanager def my_open(path, mode): f = open(path, mode) yield f f.close()-
調(diào)用
with my_open('out.txt', 'w') as f: f.write("hello , the simplest context manager") 總結(jié): Python 提供了 with 語(yǔ)法用于簡(jiǎn)化資源操作的后續(xù)清除操作,是 try/finally 的替代方法,實(shí)現(xiàn)原理建立在上下文管理器之上。此外,Python 還提供了一個(gè) contextmanager 裝飾器,更進(jìn)一步簡(jiǎn)化上下管理器的實(shí)現(xiàn)方式。
-