Python高級語法5:私有屬性、魔法屬性蹦浦、with與上下文管理器

一、私有屬性

  • 我們大家知道在類里面定義的屬性名前加兩個(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)方式店枣。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末速警,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子鸯两,更是在濱河造成了極大的恐慌闷旧,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,402評論 6 499
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件钧唐,死亡現(xiàn)場離奇詭異忙灼,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)钝侠,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,377評論 3 392
  • 文/潘曉璐 我一進(jìn)店門该园,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人机错,你說我怎么就攤上這事爬范「竿螅” “怎么了弱匪?”我有些...
    開封第一講書人閱讀 162,483評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長璧亮。 經(jīng)常有香客問我萧诫,道長,這世上最難降的妖魔是什么枝嘶? 我笑而不...
    開封第一講書人閱讀 58,165評論 1 292
  • 正文 為了忘掉前任帘饶,我火速辦了婚禮,結(jié)果婚禮上群扶,老公的妹妹穿的比我還像新娘及刻。我一直安慰自己镀裤,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,176評論 6 388
  • 文/花漫 我一把揭開白布缴饭。 她就那樣靜靜地躺著暑劝,像睡著了一般。 火紅的嫁衣襯著肌膚如雪颗搂。 梳的紋絲不亂的頭發(fā)上担猛,一...
    開封第一講書人閱讀 51,146評論 1 297
  • 那天,我揣著相機(jī)與錄音丢氢,去河邊找鬼傅联。 笑死,一個(gè)胖子當(dāng)著我的面吹牛疚察,可吹牛的內(nèi)容都是我干的蒸走。 我是一名探鬼主播,決...
    沈念sama閱讀 40,032評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼貌嫡,長吁一口氣:“原來是場噩夢啊……” “哼载碌!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起衅枫,我...
    開封第一講書人閱讀 38,896評論 0 274
  • 序言:老撾萬榮一對情侶失蹤嫁艇,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后弦撩,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體步咪,經(jīng)...
    沈念sama閱讀 45,311評論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,536評論 2 332
  • 正文 我和宋清朗相戀三年益楼,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了猾漫。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,696評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡感凤,死狀恐怖悯周,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情陪竿,我是刑警寧澤禽翼,帶...
    沈念sama閱讀 35,413評論 5 343
  • 正文 年R本政府宣布,位于F島的核電站族跛,受9級特大地震影響闰挡,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜礁哄,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,008評論 3 325
  • 文/蒙蒙 一长酗、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧桐绒,春花似錦夺脾、人聲如沸之拨。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽敦锌。三九已至,卻和暖如春佳簸,著一層夾襖步出監(jiān)牢的瞬間乙墙,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,815評論 1 269
  • 我被黑心中介騙來泰國打工生均, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留听想,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,698評論 2 368
  • 正文 我出身青樓马胧,卻偏偏與公主長得像汉买,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子佩脊,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,592評論 2 353

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

  • 包(lib)蛙粘、模塊(module) 在Python中,存在包和模塊兩個(gè)常見概念威彰。 模塊:編寫Python代碼的py...
    清清子衿木子水心閱讀 3,802評論 0 27
  • Scala與Java的關(guān)系 Scala與Java的關(guān)系是非常緊密的3瞿痢! 因?yàn)镾cala是基于Java虛擬機(jī)歇盼,也就是...
    燈火gg閱讀 3,440評論 1 24
  • 一舔痕、快捷鍵 ctr+b 執(zhí)行ctr+/ 單行注釋ctr+c ...
    o_8319閱讀 5,813評論 2 16
  • 關(guān)于端午節(jié)的由來,有許多種說法豹缀,有說是紀(jì)念春秋時(shí)期伍子胥的伯复,有說是紀(jì)念孝女曹娥的,有說起源于夏商周三代的夏至節(jié)邢笙,有...
    阿果悠悠閱讀 265評論 0 4
  • 1.7日日精進(jìn):敬畏—進(jìn)入—體驗(yàn)—交給—持續(xù) 1,缺啥補(bǔ)啥啸如,怕啥練啥; 2,一切為我所用氮惯,所用為團(tuán)隊(duì)家叮雳; 3,我...
    京心達(dá)張秀寶閱讀 114評論 0 0