zoukankan      html  css  js  c++  java
  • Python之路(第二十六篇) 面向对象进阶:内置方法

    一、__getattribute__

    object.__getattribute__(self, name)

    无条件被调用,通过实例访问属性。如果class中定义了__getattr__(),则__getattr__()不会被调用(除非显示调用或引发AttributeError异常)

      
      class Foo:
      ​
          def __init__(self,x):
              self.x = x
      ​
          def __getattr__(self, item):
              print("执行__getattr__")
      ​
          def __getattribute__(self, item):
              print("执行__getattribute__")
              # raise AttributeError("不存在的异常")
      ​
      f = Foo("nick")
      print(f.x)
      print(f.xxxx)#这里本来调用后应该在__getattribute__中产生AttributeError,然后调用__getattr__,但在#这里__getattribute__被重写了,没有产生AttributeError,所以就没调用__getattr__
    

      

    输出结果

      
      执行__getattribute__
      None
      执行__getattribute__
      None
     
    

      

    object.__getattr__(self, name)

    当一般位置找不到attribute的时候,会调用getattr,返回一个值或AttributeError异常。

      
      class Foo:
      ​
          def __init__(self,x):
              self.x = x
      ​
          def __getattr__(self, item):
              print("执行__getattr__")
      ​
          # def __getattribute__(self, item):
          #     print("执行__getattribute__")
              # raise AttributeError("不存在的异常")
      ​
      f = Foo("nick")
      print(f.x)
      print(f.xxxx) #这里是执行了新定义的__getattr__方法,并没有直接报错
    

      

    输出结果

      
      nick
      执行__getattr__
      None
     
    

      

    每次通过实例访问属性,都会经过__getattribute__函数。

    而当属性不存在时,仍然需要访问__getattribute__,不过接着要访问__getattr__。这就好像是一个异常处理函数。 需要注意的是,当使用类访问不存在的变量是,不会经过__getattr__函数。

    当属性存在时,经过__getattribute__函数,返回属性的值。

    二、__setitem__,__getitem,__delitem__

    在类中,对象以object["key"]形式访问属性或方法时调用item系列内置方法,对象以object.属性(以点的形式)调用属性或方法时调用attr系列内置方法。

      
      class Foo:
      ​
          def __init__(self,name):
              self.name = name
      ​
          def __getitem__(self, item):
              print("执行__getitem__")
              return self.__dict__[item]
      ​
          def __setitem__(self, key, value):
              print("执行__setitem__")
              self.__dict__[key] = value
      ​
          def __delitem__(self, key):
              print("执行__delitem__")
              self.__dict__.pop(key)
      ​
      f = Foo("nick")
      print(f["name"])  #获取属性, 执行__getitem__方法
      f["age"] = 18  #设置属性,执行__setitem__方法
      print(f.__dict__)
      del f["name"]  #删除属性,执行__delitem__方法
      print(f.__dict__)
    

      

    输出结果

      
      执行__getitem__
      nick
      执行__setitem__
      {'name': 'nick', 'age': 18}
      执行__delitem__
      {'age': 18}
    

      

    三、__str__,__repr__,__format__

    __str__是控制改变对象的字符串显示

    print(obj)/'%s'%obj/str(obj)时,实际上是内部调用了obj.__str__方法,如果str方法有,那么他返回的必定是一个字符串,如果没有__str__方法,会先找本类中的__repr__方法,再没有再找父类(object)中的__str__

    例子1

      class Foo:
          def __str__(self):
              return "控制打印对象时的显示形式"
      ​
      f = Foo()
      print(f) #输出结果:  控制打印对象时的显示形式
    

      

    分析:这里打印对象直接调用了被重新定义的__str__方法

    例子2

      class Foo:
          def __str__(self):
              return "控制打印对象时的显示形式"
      ​
      f = Foo()
      a = str(f)
      print(a) #输出结果:  控制打印对象时的显示形式
    

      

    分析:这里的str(object)也是调用了__str__方法

    例子3

      
      class Foo:
          def __str__(self):
              return "控制打印对象时的显示形式"
      ​
      f = Foo()
      print("%s"%f) #输出结果:  控制打印对象时的显示形式
    

      

    分析:这里的“%s”%f也是调用了__str__方法

    __repr__也是显示对象的名字方式,用repr(object)或者“%r”%object会调用__repr__方法

    repr 是str的备胎,但str不能做repr的备胎

    例子4

      class Foo:
      ​
          def __str__(self):
              return "控制打印对象时的显示形式"
      ​
          def __repr__(self):
              print("在执行repr")
              return "repr Foo"
          pass
      f = Foo()
      print("%r"%f) 
      print(f)
    

      

    输出结果

      
      在执行repr
      repr Foo
      控制打印对象时的显示形式
    

      

    分析:这里print("%r"%f) 调用重新定义的__repr__方法,print(f)调用__str__方法

    例子5

      
      class Foo:
      ​
          def __str__(self):
              return "控制打印对象时的显示形式"
      ​
          def __repr__(self):
              print("在执行repr")
              return "repr Foo"
          pass
      f = Foo()
      print(repr(f)) 
    

      

    输出结果

      在执行repr
      repr Foo
    

      

    例子6

      
      class Foo:
      ​
          # def __str__(self):
          #     return "控制打印对象时的显示形式"
      ​
          def __repr__(self):
              print("在执行repr")
              return "repr Foo"
          pass
      f = Foo()
      print(f)
    

      

    输出结果

      
      在执行repr
      repr Foo
    

      

    分析:如果要调用__str__方法,首先在本类中查找有没有__str__方法,如果没有就找本类有没有__repr__方法,如果有就执行__repr__方法,不再借用父类的__str__方法。所以repr 是str的备胎,但str不能做repr的备胎。

    例子7

      
      class Foo:
      ​
          def __str__(self):
              return "控制打印对象时的显示形式"
      ​
          # def __repr__(self):
          #     print("在执行repr")
          #     return "repr Foo"
          pass
      f = Foo()
      print(repr(f)) #输出结果 <__main__.Foo object at 0x002EB550>
    

      

    分析:要调用__repr__方法,本类没有,直接借用父类的__repr__方法,而不执行__str__方法。所以repr 是str的备胎,但str不能做repr的备胎。

    __format__

    自定制格式化字符串__format__,

      
      format_dic = {
          "ymd":"{0.year}{0.month}{0.day}",
          "dmy":"{0.day}:{0.month}:{0.year}",
          "mdy":"{0.month}-{0.day}-{0.year}"
      }
      ​
      class Foo:
      ​
          def __init__(self,year,month,day):
              self.year = year
              self.month = month
              self.day = day
      ​
          def __format__(self, format_spec):
              if not format_spec or format_spec not in format_dic:
                  format_spec = "ymd"
              fmt = format_dic[format_spec]
              return fmt.format(self)
      ​
      f = Foo(2018,8,20)
      print(format(f))
      print(format(f,"dmy"))
      print(format(f,"mdy"))
      print("{:mdy}".format(f))
    

      

    输出结果

      
      2018820
      20:8:2018
      8-20-2018
      8-20-2018
    

      

    分析:调用format方法自定制字符串格式化,这里调用__format__方法

    四、__slots__

    __slots__:其实就是将类中的名称锁定,实例化对象,只可以赋值和调用,不可以删除属性和增加新的属性

    1、__slots__是什么:是一个类变量,变量值可以是列表,元祖,或者可迭代对象,也可以是一个字符串(也就是意味着所有实例只有一个数据属性)

    2、使用点来访问属性本质就是在访问类或者对象的__dict__属性字典(类的字典是共享的,而每个实例的是独立的)

    3、字典会占用大量内存,如果你有一个属性很少的类,但是有很多实例,为了节省内存可以使用__slots__取代实例的__dict__当你定义__slots__后,__slots__就会为实例使用一种更加紧凑的内部表示。实例通过一个很小的固定大小的数组来构建,而不是为每个实例定义一个字典,这跟元组或列表很类似。在__slots__中列出的属性名在内部被映射到这个数组的指定小标上。使用__slots__一个不好的地方就是我们不能再给实例添加新的属性了,只能使用在__slots__中定义的那些属性名。

    4、应用场景:当实例化几万个对象的时候,每个对象都会生成一个名称空间__dict__,而每一个名称空间都会各自占用一个内存,造成内存的浪费,用 __slots__,不用再产生每个对象的 dict 了,省内存,对象的 dict 都统一用类的 dict,属性都是用 slots 给定义的

      
      class Foo:
      ​
          __slots__ = ["name","age"]  #在类中定义属性"name"、"age"
      ​
      f1 = Foo()
      f1.name = "nick" #首先要进行赋值,不赋值调用会出错
      print(f1.name)
      # f1.gender = "female"  # 新增__slots__之外的属性会报错
      # del f1.age   #删除属性也会报错
    

      

    五、__doc__

    __doc__显示文档注释信息,文档注释信息无法继承给子类。

    例子

      
      class Foo:
          """
          我是文档注释
          """
          pass
      ​
      f = Foo()
      print(f.__doc__) #输出结果 :    我是文档注释
    

      

    例子2

      
      class Foo:
          """
          我是文档注释
          """
          pass
      ​
      class Son(Foo):
          pass
      ​
      s = Son()
      print(s.__doc__)  #输出结果:None
     
    

      

    六、__module____class__

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

      __class__ 表示当前操作的对象的类是什么

    例子1

      
      class Foo:
      ​
          def __init__(self,name):
              self.name = name
      ​
      f = Foo("nick")
      print(f.__module__) #当执行文件为当前文件时,__module__ = __main__
      #输出结果:__main__
      print(f.__class__)  #输出当前操作的类名是什么
      #输出结果:<class '__main__.Foo'>
    

      

    例子2

    文件结构

      ├── index.py #执行文件
    │ ├── test
    │ │   ├── a.py  

    a.py内容:

      
      class Bar:
          def __init__(self):
              self.name = "nicholas"
    

      

    index.py内容:

     from test.a import  Bar
      ​
      class Foo:
      ​
          def __init__(self,name):
              self.name = name
      ​
      b = Bar()
      print(b.name)  #输出结果 nicholas
      print(b.__module__)  #输出结果 test.a ,表示当前操作的类在test.a下
      print(b.__class__)  #表示当前操作的类名是<class 'test.a.Bar'>
     
    

      

    七、 __del__析构方法

    析构方法,当对象在内存中被释放时,自动触发执行。即清除释放内存的方法。

    这个是在回收实例化对象时触发执行的方法,删除对象的属性不会触发__del__方法

    注:此方法一般无须定义,因为Python是一门高级语言,程序员在使用时无需关心内存的分配和释放,因为此工作都是交给Python解释器来执行,所以,析构函数的调用是由解释器在进行垃圾回收时自动触发执行的。

    例子1

      
      class Foo:
          def __init__(self,name):
              self.name = name
      ​
          def __del__(self):
              print("我执行啦!")
      ​
      f = Foo("nick")
      print("-----1")
      del f.name
      print("-----2")   #这里是先执行print("-----1")然后执行print("-----2") ,
      # 执行del f.name只是删除对象字典的"name"属性,并不会触发__del__方法
    

      

    输出结果

      
      -----1
      -----2
      我执行啦!
    

      

    例子2

      
      class Foo:
          def __init__(self,name):
              self.name = name
      ​
          def __del__(self):
              print("我执行啦!")
      ​
      f = Foo("nick")
      print("-----1")
      del f   #删除对象才能调用__del__,这里既执行了__del__方法,又删除了对象
      print("-----2")   
    

      

    输出结果

      
      -----1
      我执行啦!
      -----2
     
    

      

    例子3

      
      class Foo:
          def __init__(self,name):
              self.name = name
      ​
          def __del__(self):
              print("我执行啦!")
      ​
      f = Foo("nick")
      import time
      time.sleep(2)
    

      

    输出结果

    
    
     我执行啦!
    

      

    分析:这里在程序结束时,python解释器自动回收内存,执行了__del__方法

    典型的应用场景:

    创建数据库类,用该类实例化出数据库链接对象,对象本身是存放于用户空间内存中,而链接则是由操作系统管理的,存放于内核空间内存中

    当程序结束时,python只会回收自己的内存空间,即用户态内存,而操作系统的资源则没有被回收,这就需要我们定制__del__,在对象被删除前向操作系统发起关闭数据库链接的系统调用,回收资源。

    例子

      
      class Foo:
      ​
          def __del__(self):
              print("我执行了")
      ​
      f = Foo()
      f.file  = open("test.txt")   #打开文件,在操作系统中打开了一个文件,
      # 拿到了文件操作符存在了内存中
      del f.file   #删除了内存中的文件操作符,但是并未关闭操作系统中的文件,
      # 这时需要在__del__中加入定制f.file.close(),关闭操作系统的文件
      print("-----1")
      #最后程序结束python解释器自动执行内存回收,执行了__del__方法
    

      

    输出结果

      
      -----1
      我执行了
     
    

      

  • 相关阅读:
    【webrtc】PTCPeerConnection(28)
    windows传文件到linux服务器--- secureCRT PK xftp
    Navicat premium查看数据库表中文注释的两种方式
    Jmeter 5.1实现图片上传接口测试
    查看服务器内存、CPU、网络等占用情况的命令--汇总
    AWS服务器上安全组端口设置和访问的问题
    linux服务器时间乱码问题解决
    连接linux的几款工具 简介
    服务器上build.xml文件乱码解决(亲测有效)
    jmeter通过ant执行时报错 jmeter.log not found
  • 原文地址:https://www.cnblogs.com/Nicholas0707/p/9440476.html
Copyright © 2011-2022 走看看