修改、查看私有屬性、名字重整
如下,Test類(lèi)定義 一個(gè)私有屬性 __name? 實(shí)例化一個(gè)對(duì)象 a ,無(wú)法調(diào)用該屬性,
打印 a.__dict__( 可以檢查一個(gè)對(duì)象的所有屬性 )查看?,發(fā)現(xiàn)__name存在并且名字變?yōu)? _Test__name (無(wú)法調(diào)用的原因,名字被改變)
改變規(guī)則:私有屬性前添加類(lèi)名,再在類(lèi)名前添加一個(gè)下劃線(xiàn) ( 名字重整 )
我們驗(yàn)證一下,打印修改后的屬性,如下
?
這里有個(gè)疑問(wèn),既然無(wú)法直接打印,那我們?yōu)槭裁纯梢灾苯有薷模?
修改測(cè)試一下,打印輸出,此時(shí)__name 并不是私有屬性了,此時(shí)的 __name 只是添加了一個(gè)屬性,可以直接調(diào)用
這里就沒(méi)有什么私有公有之說(shuō)了,所謂的公有私有,是指類(lèi)中的屬性,實(shí)例對(duì)象無(wú)法直接調(diào)用
另外補(bǔ)充一下 __dict__屬性,Test類(lèi)也是一個(gè)對(duì)象,查看其屬性:每個(gè)魔法屬性對(duì)應(yīng)值或函數(shù)
?
?
魔法屬性、方法
無(wú)論人或事物往往都有不按套路出牌的情況,Python的類(lèi)屬性也是如此。存在一些具有特殊含義的屬性
如上圖,對(duì)應(yīng)的魔法屬性:
?
1. __doc__ :表示類(lèi)的描述信息,help()方法也可以輸出類(lèi)的描述信息
class Test():
"""
This is a description of a class
"""
pass
print(Test.__doc__)
print("*" * 15)
print(help(Test))
?
2. __module__? 和 __class__ :__module__表示當(dāng)前操作在哪個(gè)模塊,__class__表示當(dāng)前操作的對(duì)象的類(lèi)是什么
# -*- coding:utf-8 -*-
# test.py
class Person(object):
def __init__(self):
self.name = 'laowang'
from test import Person
# main.py
obj = Person()
print(obj.__module__) # 輸出創(chuàng)建該對(duì)象的對(duì)應(yīng)模塊 test
print(obj.__class__) # 輸出創(chuàng)建該對(duì)象的對(duì)應(yīng)模塊中的類(lèi) test.Person
?
3. __init__ :初始化方法(不建議稱(chēng)其為構(gòu)造方法,可以說(shuō)__init__ 和 __new__ 方法一起完成了構(gòu)造方法的功能),通過(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__ 方法
?
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
?
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__
?
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__':
, '__module__': '__main__', 'country': 'China', '__doc__': None, '__weakref__':
, 'func':
, '__init__':
}
obj1 = Province('山東', 10000)
print(obj1.__dict__)
# 獲取 對(duì)象obj1 的屬性
# 輸出:{'count': 10000, 'name': '山東'}
obj2 = Province('山西', 20000)
print(obj2.__dict__)
# 獲取 對(duì)象obj1 的屬性
# 輸出:{'count': 20000, 'name': '山西'}
?
7.?__str__:如果一個(gè)類(lèi)中定義了__str__方法,那么在打印 對(duì)象 時(shí),默認(rèn)輸出該方法的返回值
class Foo:
def __str__(self):
return 'laowang'
obj = Foo()
print(obj)
# 輸出:laowang
?
8.?__getitem__、__setitem__、__delitem__ :用于索引操作,如字典。以上分別表示獲取、設(shè)置、刪除數(shù)據(jù)
# -*- coding:utf-8 -*-
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__
9.?__getitem__、__setitem__、__delitem__ :該三個(gè)方法用于分片操作,如:列表
# -*- coding:utf-8 -*-
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、上下文管理器
對(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à),就可能會(huì)出現(xiàn) "Can not connect to MySQL server Too many connections",因?yàn)閿?shù)據(jù)庫(kù)連接是一種非常昂貴的資源,不可能無(wú)限制的被創(chuàng)建。
普通版:
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)階版:
def m2():
f = open("output.txt", "w")
try:
f.write("python之禪")
except IOError: # except 未捕獲到對(duì)應(yīng)異常,依然會(huì)崩
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í)版:
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) 。
?
上下文(context)
上下文在不同的地方表示不同的含義,要感性理解。context其實(shí)說(shuō)白了,和文章的上下文是一個(gè)意思,通俗一點(diǎn),理解為環(huá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:
# 實(shí)例化File對(duì)象,執(zhí)行__init__方法,filename屬性對(duì)應(yīng)out.txt,w對(duì)應(yīng)mode屬性
# with判斷其是否是一個(gè)上下文管理器(實(shí)現(xiàn)對(duì)應(yīng)的方法),然后自動(dòng)調(diào)用 __enter__()方法
# __enter__()方法返回值是什么,對(duì)應(yīng)的 f 就是什么,而當(dāng)結(jié)束或者出現(xiàn)異常時(shí),會(huì)自動(dòng)調(diào)用 __exit__()
# 方法
print("writing")
f.write('hello, python')
這樣,你就無(wú)需顯示地調(diào)用 close 方法了,由系統(tǒng)自動(dòng)去調(diào)用,哪怕中間遇到異常 close 方法也會(huì)被調(diào)用。
?
實(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)方式。
?
?
?
?
更多文章、技術(shù)交流、商務(wù)合作、聯(lián)系博主
微信掃碼或搜索:z360901061
微信掃一掃加我為好友
QQ號(hào)聯(lián)系: 360901061
您的支持是博主寫(xiě)作最大的動(dòng)力,如果您喜歡我的文章,感覺(jué)我的文章對(duì)您有幫助,請(qǐng)用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點(diǎn)擊下面給點(diǎn)支持吧,站長(zhǎng)非常感激您!手機(jī)微信長(zhǎng)按不能支付解決辦法:請(qǐng)將微信支付二維碼保存到相冊(cè),切換到微信,然后點(diǎn)擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對(duì)您有幫助就好】元

