一、私有屬性
-
我們大家知道在類里面定義的屬性名前加兩個(gè)下劃線就是私有屬性撞蜂,它是不能在外面被訪問的盲镶,如下:
class Person(object): def __init__(self,name): self.__name = name person = Person("小王") print(person.__name)
打印結(jié)果:報(bào)錯(cuò),
__name
是私有的蝌诡,外面是無法訪問的AttributeError: 'Person' object has no attribute '__name'
分析上面報(bào)錯(cuò)的原因是:
__name
被Python改了名字溉贿,一種假象而已,我們可以看看Python把它改成了什么,我們通過魔法屬性person.__dict__
來打印print(person.__dict__)
打印結(jié)果是:
{'_Person__name': '小王'}
看到上面的打印浦旱,其實(shí)Python是把私有的屬性改成了 一個(gè)下劃線+類名+私有屬性 :
_類名私有屬性
,如上面的__name
->_Person__name
,那么我們就可以通過實(shí)例對象.被改后的私有屬性名
來打印私有屬性宇色,如下print(person._Person__name)
打印結(jié)果是:
小王
二、魔法屬性
無論人或事物往往都有不按套路出牌的情況颁湖,Python的類屬性也是如此宣蠕,存在著一些具有特殊含義的屬性,詳情如下:
-
2.1甥捺、
__doc__
: 表示類的描述信息class Foo: """ 描述類信息抢蚀,這是用于看片的神奇 """ def func(self): pass print(Foo.__doc__) #輸出:類的描述信息
-
2.2、
__module__
和__class__
__module__
表示當(dāng)前操作的對象在那個(gè)模塊__class__
表示當(dāng)前操作的對象的類是什么,也就是打印類對象的名字-
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 即:輸出類
-
2.3镰禾、
__init__
: 初始化方法皿曲,通過類創(chuàng)建對象時(shí),自動(dòng)觸發(fā)執(zhí)行class Person: def __init__(self, name): self.name = name self.age = 18 obj = Person('laowang') # 自動(dòng)執(zhí)行類中的 __init__ 方法
-
2.4吴侦、
__del__
:當(dāng)對象在內(nèi)存中被釋放時(shí)屋休,自動(dòng)觸發(fā)執(zhí)行。
提示:此方法一般無須定義备韧,因?yàn)镻ython是一門高級語言劫樟,程序員在使用時(shí)無需關(guān)心內(nèi)存的分配和釋放,因?yàn)榇斯ぷ鞫际墙唤oPython解釋器來執(zhí)行,所以毅哗,__del__
的調(diào)用是由解釋器在進(jìn)行垃圾回收時(shí)自動(dòng)觸發(fā)執(zhí)行的听怕。class Foo: def __del__(self): pass
-
2.5、
__call__
: 對象后面加括號虑绵,觸發(fā)執(zhí)行
提示:__init__
方法的執(zhí)行是由創(chuàng)建對象觸發(fā)的尿瞭,即:對象 = 類名()
;而對于__call__
方法的執(zhí)行是由對象后加括號觸發(fā)的翅睛,即:對象()
或者類()()
class Foo: def __init__(self): pass def __call__(self, *args, **kwargs): print('__call__') obj = Foo() # 執(zhí)行 __init__ obj() # 執(zhí)行 __call__
-
2.6声搁、
__dict__
: 類或?qū)ο笾械乃袑傩?類的實(shí)例屬性屬于對象;類中的類屬性和方法等屬于類class Province(object): country = 'China' def __init__(self, name, count): self.name = name self.count = count def func(self, *args, **kwargs): print('func') # 獲取類的屬性捕发,即:類屬性疏旨、方法、 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__) # 獲取 對象obj1 的屬性 # 輸出:{'count': 10000, 'name': '山東'} obj2 = Province('山西', 20000) print(obj2.__dict__) # 獲取 對象obj1 的屬性 # 輸出:{'count': 20000, 'name': '山西'}
-
2.7扎酷、
__str__
: 如果一個(gè)類中定義了__str__
方法檐涝,那么在打印 對象 時(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答渔、對于 系統(tǒng)資源如文件页畦、數(shù)據(jù)庫連接、socket 而言研儒,應(yīng)用程序打開這些資源并執(zhí)行完業(yè)務(wù)邏輯之后豫缨,必須做的一件事就是要關(guān)閉(斷開)該資源。
比如 Python 程序打開一個(gè)文件端朵,往文件中寫內(nèi)容好芭,寫完之后,就要關(guān)閉該文件冲呢,否則會出現(xiàn)什么情況呢舍败?極端情況下會出現(xiàn) "Too many open files" 的錯(cuò)誤,因?yàn)橄到y(tǒng)允許你打開的最大文件數(shù)量是有限的。同樣邻薯,對于數(shù)據(jù)庫裙戏,如果連接數(shù)過多而沒有及時(shí)關(guān)閉的話,就可能會出現(xiàn) "Can not connect to MySQL server Too many connections"厕诡,因?yàn)閿?shù)據(jù)庫連接是一種非常昂貴的資源累榜,不可能無限制的被創(chuàng)建。
-
3.2灵嫌、看看如何正確關(guān)閉一個(gè)文件壹罚。
-
普通版:打開文件之后直接進(jìn)行讀寫操作
def m1(): f = open("output.txt", "w") f.write("python之禪") f.close()
這樣寫有一個(gè)潛在的問題,如果在調(diào)用 write 的過程中寿羞,出現(xiàn)了異常進(jìn)而導(dǎo)致后續(xù)代碼無法繼續(xù)執(zhí)行猖凛,close 方法無法被正常調(diào)用,因此資源就會一直被該程序占用者釋放绪穆。那么該如何改進(jìn)代碼呢辨泳?
-
進(jìn)階版: 看著不夠簡潔哈,后面還有高級版
def m2(): f = open("output.txt", "w") try: f.write("python之禪") except IOError: print("oops error") finally: f.close()
改良版本的程序是對可能發(fā)生異常的代碼處進(jìn)行 try 捕獲玖院,使用
try/finally
語句菠红,該語句表示如果在 try 代碼塊中程序出現(xiàn)了異常,后續(xù)代碼就不再執(zhí)行司恳,而直接跳轉(zhuǎn)到 except 代碼塊途乃。而無論如何绍傲,finally 塊的代碼最終都會被執(zhí)行扔傅。因此,只要把 close 放在 finally 代碼中烫饼,文件就一定會關(guān)閉猎塞。 -
高級版: 這么看起來就非常爽了
def m3(): with open("output.txt", "r") as f: f.write("Python之禪")
一種更加簡潔、優(yōu)雅的方式就是用 with 關(guān)鍵字杠纵。open 方法的返回值賦值給變量 f荠耽,當(dāng)離開 with 代碼塊的時(shí)候,系統(tǒng)會自動(dòng)調(diào)用 f.close() 方法比藻, with 的作用和使用 try/finally 語句是一樣的铝量。那么它的實(shí)現(xiàn)原理是什么?在講 with 的原理前要涉及到另外一個(gè)概念银亲,就是上下文管理器(Context Manager)慢叨。
-
-
3.3、上下文管理器(Context Manager)
-
什么是上下文(context)务蝠?
- 看拍谐,一篇文章,給你摘錄一段,沒前沒后轩拨,你讀不懂践瓷,因?yàn)橛姓Z境,就是語言環(huán)境存在亡蓉,一段話說了什么晕翠,要通過上下文(文章的上下文)來推斷。
- app點(diǎn)擊一個(gè)按鈕進(jìn)入一個(gè)新的界面寸宵,也要保存你是在哪個(gè)屏幕跳過來的等等信息崖面,以便你點(diǎn)擊返回的時(shí)候能正確跳回,如果不存肯定就無法正確跳回了梯影。
- 看這些都是上下文的典型例子巫员,理解成環(huán)境就可以,(而且上下文雖然叫上下文甲棍,但是程序里面一般都只有上文而已简识,只是叫的好聽叫上下文。感猛。進(jìn)程中斷在操作系統(tǒng)中是有上有下的七扰,不過不這個(gè)高深的問題就不要深究了。陪白。颈走。)
-
上下文管理器
任何實(shí)現(xiàn)了__enter__()
和__exit__()
方法的對象都可稱之為上下文管理器,上下文管理器對象可以使用with
關(guān)鍵字咱士。顯然立由,文件(file
)對象也實(shí)現(xiàn)了上下文管理器。
那么文件對象是如何實(shí)現(xiàn)這兩個(gè)方法的呢序厉?我們可以模擬實(shí)現(xiàn)一個(gè)自己的文件類锐膜,讓該類實(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__()
方法返回資源對象弛房,這里就是你將要打開的那個(gè)文件對象道盏,__exit__()
方法處理一些清除工作。
因?yàn)?File 類實(shí)現(xiàn)了上下文管理器文捶,現(xiàn)在就可以使用 with 語句了荷逞。with File('out.txt', 'w') as f: print("writing") f.write('hello, python')
提示:
File('out.txt', 'w')
去執(zhí)行的時(shí)候就會去調(diào)用里面的_enter__
方法返回一個(gè)操作文本的對象,當(dāng)在進(jìn)行寫的操作的時(shí)候粹排,如果出現(xiàn)了異常种远,會自動(dòng)調(diào)用__exit__
,釋放響應(yīng)的資源,這樣恨搓,你就無需顯示地調(diào)用 close 方法了院促,由系統(tǒng)自動(dòng)去調(diào)用筏养,哪怕中間遇到異常 close 方法也會被調(diào)用。
-
-
3.4常拓、實(shí)現(xiàn)上下文管理器的另外方式
Python 還提供了一個(gè)contextmanager
的裝飾器渐溶,更進(jìn)一步簡化了上下文管理器的實(shí)現(xiàn)方式。通過 yield 將函數(shù)分割成兩部分弄抬,yield 之前的語句在__enter__
方法中執(zhí)行茎辐,yield 之后的語句在__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 語法用于簡化資源操作的后續(xù)清除操作拖陆,是 try/finally 的替代方法,實(shí)現(xiàn)原理建立在上下文管理器之上懊亡。此外依啰,Python 還提供了一個(gè) contextmanager 裝飾器,更進(jìn)一步簡化上下管理器的實(shí)現(xiàn)方式店枣。
-