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
  我执行了

  

原文地址:https://www.cnblogs.com/Nicholas0707/p/9440476.html

时间: 2024-10-07 04:18:14

Python之路(第二十六篇) 面向对象进阶:内置方法的相关文章

第二十六篇 面向对象初识

一. 三大编程范式 前面学完了Python的基本语法,能写Python代码, 而且可以处理工作中的一些问题,今天开始就要进入面向对象的学习了.首先,了解下三大编程范式,编程范式就是编程方法论,表明的是一种编程风格. 切记:三种编程风格没有好坏之分,有分别的是使用不同风格的人. 1. 面向过程编程 核心是过程二字,过程指的是解决问题的步骤,即先干什么再干什么......面向过程的设计就好比精心设计好一条流水线,是一种机械式的思维方式. 优点是:复杂度的问题流程化,进而简单化(一个复杂的问题,分成一

面向对象进阶——内置方法(二)

七.__setitem__, __getitem__, __delitem__ item系列 class Foo: def __init__(self, name): self.name = name def __getitem__(self, item): # print('getitem...') #print(item) return self.__dict__.get(item) # 字典get方法有则取值,无也不会报错 def __setitem__(self, key, value)

Python开发【第二十二篇】:Web框架之Django【进阶】

Python开发[第二十二篇]:Web框架之Django[进阶] 猛击这里:http://www.cnblogs.com/wupeiqi/articles/5246483.html 博客园 首页 新随笔 联系 订阅 管理 随笔-124  文章-127  评论-205 Python之路[第十七篇]:Django[进阶篇 ] Model 到目前为止,当我们的程序涉及到数据库相关操作时,我们一般都会这么搞: 创建数据库,设计表结构和字段 使用 MySQLdb 来连接数据库,并编写数据访问层代码 业务逻

Python之路【第六篇】:socket

Python之路[第六篇]:socket Socket socket通常也称作"套接字",用于描述IP地址和端口,是一个通信链的句柄,应用程序通常通过"套接字"向网络发出请求或者应答网络请求. socket起源于Unix,而Unix/Linux基本哲学之一就是"一切皆文件",对于文件用[打开][读写][关闭]模式来操作.socket就是该模式的一个实现,socket即是一种特殊的文件,一些socket函数就是对其进行的操作(读/写IO.打开.关闭

Python开发【第十六篇】:AJAX全套

Python开发[第十六篇]:AJAX全套 概述 对于WEB应用程序:用户浏览器发送请求,服务器接收并处理请求,然后返回结果,往往返回就是字符串(HTML),浏览器将字符串(HTML)渲染并显示浏览器上. 1.传统的Web应用 一个简单操作需要重新加载全局数据 2.AJAX AJAX,Asynchronous JavaScript and XML (异步的JavaScript和XML),一种创建交互式网页应用的网页开发技术方案. 异步的JavaScript:使用 [JavaScript语言] 以

第二十六篇:USB3.0高带宽ISO(48KBytes/125us)实战

USB3.1技术已经推出, 10Gbps的速率足以满足数据, HD视频传输的要求. 要步入USB3.1的研发, 还得将USB3.0的基础打扎实. 微软提供的SUPER MUTT只包含一个接口0, 其下有两个ALT, ALT 1与ALT 2, 分别包含了两对ISO IN/OUT端点, 不过, 只有ALT 2下的ISO OUT EP的bMaxBurst为1, 而其它三个ISO EP的bMaxBurst均为0, 而所有的ISO EP的Mult均为0. 即只有一个ISO EP支持2KBytes/125u

Python中使用help查看某一类对象的内置方法

Python中不同类型的对象有不同的方法,那么如何查看某一类型对象的方法?我们可以使用help()函数 用法help() ,括号中写对象的类型.比如查看数据类型的方法: help(int)  |  Methods defined here:  |    |  __abs__(...)  |      x.__abs__() <==> abs(x)  |    |  __add__(...)  |      x.__add__(y) <==> x+y  |    |  __and__

4-19 面向对象 的内置方法

1,__str__ 和 __repr__ __str__就是改变对象的字符串显示. Python有一个内置的函数叫repr,它能把一个对象用字符串的形式表达出来以便辨认,这就是"字符串表示形式".repr就是通过__repr__这个特殊方法来得到一个对象的字符串表示形式.如果没有实现__repr__,当我们再控制台里打印一个变量的实例时,得到的字符串可能会是<__main__.Object at 0x14af07dbe80>,这个表示的是打印的对象,以及对象的内存地址. s

Python之路【第六篇】python基础 之面向对象(一)

一.三大编程范式 1.面向过程编程 2.函数式编程 3.面向对象编程 二.编程进化论 1.编程最开始就是无组织无结构,从简单控制流中按步写指令 2.从上述的指令中提取重复的代码块或逻辑,组织到一起(比方说,你定义了一个函数),便实现了代码重用,且代码由无结构走向了结构化,创建程序的过程变得更具逻辑性 3.我们定义函数都是独立于函数外定义变量,然后作为参数传递给函数,这意味着:数据与动作是分离的 4.如果我们把数据和动作内嵌到一个结构(函数或类)里面,那么我们就有了一个'对象系统'(对象就是数据与