Python高级语法5:私有属性、魔法属性、with与上下文管理器

一、私有属性

  • 我们大家知道在类里面定义的属性名前加两个下划线就是私有属性,它是不能在外面被访问的,如下:

    class Person(object):
    
          def __init__(self,name):
                self.__name = name
    
    person = Person("小王")
    print(person.__name)
    

    打印结果:报错,__name是私有的,外面是无法访问的

    AttributeError: 'Person' object has no attribute '__name'
    

    分析上面报错的原因是:__name被Python改了名字,一种假象而已,我们可以看看Python把它改成了什么,我们通过魔法属性 person.__dict__ 来打印

    print(person.__dict__)
    

    打印结果是:

    {'_Person__name': '小王'}
    

    看到上面的打印,其实Python是把私有的属性改成了 一个下划线+类名+私有属性_类名私有属性,如上面的__name -> _Person__name,那么我们就可以通过 实例对象.被改后的私有属性名 来打印私有属性,如下

    print(person._Person__name)
    

    打印结果是:小王

二、魔法属性

无论人或事物往往都有不按套路出牌的情况,Python的类属性也是如此,存在着一些具有特殊含义的属性,详情如下:

  • 2.1、__doc__ : 表示类的描述信息

    class Foo:
         """ 描述类信息,这是用于看片的神奇 """
         def func(self):
               pass
    
    print(Foo.__doc__)
    #输出:类的描述信息
    
  • 2.2、__module____class__

    • __module__ 表示当前操作的对象在那个模块

    • __class__ 表示当前操作的对象的类是什么,也就是打印类对象的名字

    • 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__ : 初始化方法,通过类创建对象时,自动触发执行

    class Person:
          def __init__(self, name):
                self.name = name
                self.age = 18
    
    obj = Person('laowang')  # 自动执行类中的 __init__ 方法
    
  • 2.4、__del__ :当对象在内存中被释放时,自动触发执行。
    提示:此方法一般无须定义,因为Python是一门高级语言,程序员在使用时无需关心内存的分配和释放,因为此工作都是交给Python解释器来执行,所以,__del__的调用是由解释器在进行垃圾回收时自动触发执行的。

    class Foo:
          def __del__(self):
                pass
    
  • 2.5、__call__ : 对象后面加括号,触发执行
    提示:__init__方法的执行是由创建对象触发的,即:对象 = 类名();而对于__call__方法的执行是由对象后加括号触发的,即:对象() 或者 类()()

    class Foo:
          def __init__(self):
                 pass
    
          def __call__(self, *args, **kwargs):
                 print('__call__')
    
    
    obj = Foo()  # 执行 __init__
    obj()  # 执行 __call__
    
  • 2.6、__dict__ : 类或对象中的所有属性,类的实例属性属于对象;类中的类属性和方法等属于类

    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__: 如果一个类中定义了 __str__ 方法,那么在打印 对象 时,默认输出该方法的返回值

    class Foo:
          def __str__(self):
                return '小李'
    
    obj = Foo()
    print(obj)
    # 输出:小李
    
  • 2.8、__getitem____setitem____delitem__ : 用于索引操作,如字典。以上分别表示 获取、设置、删除 数据

    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']      # 自动触发执行 __getitem__
    obj['k2'] = 'laowang'   # 自动触发执行 __setitem__
    del obj['k1']           # 自动触发执行 __delitem__
    
  • 2.9、__getslice____setslice____delslice__: 该三个方法用于分片操作,如:列表

    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]                   # 自动触发执行 __getslice__
    obj[0:1] = [11,22,33,44]    # 自动触发执行 __setslice__
    del obj[0:2]                # 自动触发执行 __delslice__
    

三、with与上下文管理器”

  • 3.1、对于 系统资源如文件、数据库连接、socket 而言,应用程序打开这些资源并执行完业务逻辑之后,必须做的一件事就是要关闭(断开)该资源
    比如 Python 程序打开一个文件,往文件中写内容,写完之后,就要关闭该文件,否则会出现什么情况呢?极端情况下会出现 "Too many open files" 的错误,因为系统允许你打开的最大文件数量是有限的。

    同样,对于数据库,如果连接数过多而没有及时关闭的话,就可能会出现 "Can not connect to MySQL server Too many connections",因为数据库连接是一种非常昂贵的资源,不可能无限制的被创建。

  • 3.2、看看如何正确关闭一个文件。

    • 普通版:打开文件之后直接进行读写操作

      def m1():
           f = open("output.txt", "w")
           f.write("python之禅")
           f.close()
      

      这样写有一个潜在的问题,如果在调用 write 的过程中,出现了异常进而导致后续代码无法继续执行,close 方法无法被正常调用,因此资源就会一直被该程序占用者释放。那么该如何改进代码呢?

    • 进阶版: 看着不够简洁哈,后面还有高级版

      def m2():
           f = open("output.txt", "w")
           try:
                f.write("python之禅")
           except IOError:
                print("oops error")
           finally:
                f.close()
      

      改良版本的程序是对可能发生异常的代码处进行 try 捕获,使用 try/finally 语句,该语句表示如果在 try 代码块中程序出现了异常,后续代码就不再执行,而直接跳转到 except 代码块。而无论如何,finally 块的代码最终都会被执行。因此,只要把 close 放在 finally 代码中,文件就一定会关闭

    • 高级版: 这么看起来就非常爽了

      def m3():
           with open("output.txt", "r") as f:
                  f.write("Python之禅")
      

      一种更加简洁、优雅的方式就是用 with 关键字。open 方法的返回值赋值给变量 f,当离开 with 代码块的时候,系统会自动调用 f.close() 方法, with 的作用和使用 try/finally 语句是一样的。那么它的实现原理是什么?在讲 with 的原理前要涉及到另外一个概念,就是上下文管理器(Context Manager)

  • 3.3、上下文管理器(Context Manager)

    • 什么是上下文(context)?

      • 看,一篇文章,给你摘录一段,没前没后,你读不懂,因为有语境,就是语言环境存在,一段话说了什么,要通过上下文(文章的上下文)来推断。
      • app点击一个按钮进入一个新的界面,也要保存你是在哪个屏幕跳过来的等等信息,以便你点击返回的时候能正确跳回,如果不存肯定就无法正确跳回了。
      • 看这些都是上下文的典型例子,理解成环境就可以,(而且上下文虽然叫上下文,但是程序里面一般都只有上文而已,只是叫的好听叫上下文。。进程中断在操作系统中是有上有下的,不过不这个高深的问题就不要深究了。。。)
    • 上下文管理器
      任何实现了 __enter__()__exit__() 方法的对象都可称之为上下文管理器,上下文管理器对象可以使用 with 关键字。显然,文件(file)对象也实现了上下文管理器。
      那么文件对象是如何实现这两个方法的呢?我们可以模拟实现一个自己的文件类,让该类实现 __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__()方法返回资源对象,这里就是你将要打开的那个文件对象,__exit__()方法处理一些清除工作。
      因为 File 类实现了上下文管理器,现在就可以使用 with 语句了。

      with File('out.txt', 'w') as f:
               print("writing")
               f.write('hello, python')
      

      提示:File('out.txt', 'w')去执行的时候就会去调用里面的_enter__方法返回一个操作文本的对象,当在进行写的操作的时候,如果出现了异常,会自动调用__exit__,释放响应的资源,这样,你就无需显示地调用 close 方法了,由系统自动去调用,哪怕中间遇到异常 close 方法也会被调用。

  • 3.4、实现上下文管理器的另外方式
    Python 还提供了一个 contextmanager 的装饰器,更进一步简化了上下文管理器的实现方式。通过 yield 将函数分割成两部分,yield 之前的语句在 __enter__ 方法中执行,yield 之后的语句在 __exit__ 方法中执行。紧跟在 yield 后面的值是函数的返回值。

    from contextlib import contextmanager
    
    @contextmanager
    def my_open(path, mode):
          f = open(path, mode)
          yield f
          f.close()
    
    • 调用

      with my_open('out.txt', 'w') as f:
              f.write("hello , the simplest context manager")
      
    • 总结: Python 提供了 with 语法用于简化资源操作的后续清除操作,是 try/finally 的替代方法,实现原理建立在上下文管理器之上。此外,Python 还提供了一个 contextmanager 装饰器,更进一步简化上下管理器的实现方式。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,937评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,503评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,712评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,668评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,677评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,601评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,975评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,637评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,881评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,621评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,710评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,387评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,971评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,947评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,189评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,805评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,449评论 2 342

推荐阅读更多精彩内容

  • 包(lib)、模块(module) 在Python中,存在包和模块两个常见概念。 模块:编写Python代码的py...
    清清子衿木子水心阅读 3,798评论 0 27
  • Scala与Java的关系 Scala与Java的关系是非常紧密的!! 因为Scala是基于Java虚拟机,也就是...
    灯火gg阅读 3,417评论 1 24
  • 一、快捷键 ctr+b 执行ctr+/ 单行注释ctr+c ...
    o_8319阅读 5,779评论 2 16
  • 关于端午节的由来,有许多种说法,有说是纪念春秋时期伍子胥的,有说是纪念孝女曹娥的,有说起源于夏商周三代的夏至节,有...
    阿果悠悠阅读 264评论 0 4
  • 1.7日日精进:敬畏—进入—体验—交给—持续 1,缺啥补啥,怕啥练啥; 2,一切为我所用,所用为团队家; 3,我...
    京心达张秀宝阅读 109评论 0 0