【9.0】Python面向对象之内置方法

【引入】

  • Python的Class机制内置了很多特殊的方法来帮助使用者高度定制自己的类
  • 这些内置方法都是以双下划线开头和结尾的,会在满足某种条件时自动触发
__init__ :初始化类时触发 __del__ :删除类时触发 __new__ :构造类时触发 __str__ :str函数或者print函数触发 __repr__ :repr或者交互式解释器触发 __doc__ :打印类内的注释内容 __enter__ :打开文档触发 __exit__ :关闭文档触发 __getattr__ : 访问不存在的属性时调用 __setattr__ :设置实例对象的一个新的属性时调用 __delattr__ :删除一个实例对象的属性时调用 __setitem__ :列表添加值 __getitem__ :将对象当作list使用 __delitem__ :列表删除值 __call__ :对象后面加括号,触发执行 __iter__ :迭代器

【一】__init__(),__del__()__,__new__()

  • Python的Class机制内置了很多特殊的方法来帮助使用者高度定制自己的类

  • 这些内置方法都是以双下划线开头和结尾的,会在满足某种条件时自动触发

  • 这个方法是一个类的构造函数,与之对应的__del__()是析构函数,通过此方法我们可以定义一个对象的初始操作。

  • 但实际上,新式类的__new__()才是真正的初始化函数。

class A(object): def __init__(self): print('__init__') def __new__(cls, *args, **kwargs): print('__new__') # cls表示一个类,一个当前要被实例化的类,参数由py解释器自动提供 return super().__new__(cls, *args, **kwargs) def __del__(self): print('__del__') a = A() print('do something') # __new__ # __init__ # do something # __del__ # 实际上, # __new__()负责创建一个对象, # __init__负责定制化该对象,即是在对象创建好之后初始化变量
  • 既然知道了__new__()方法,我们是不是可以考虑一下,如何应用它呢?最常见的就是单例模式了,下面给出实现实例。
class Singleton(object): _instance = None def __new__(cls, *args, **kwargs): """ 注意这实际上是一个类方法, cls 表示当前类 :param args: :param kwargs: :return: """ if cls._instance is None: cls._instance = super().__new__(cls, *args, **kwargs) return cls._instance s1 = Singleton() s2 = Singleton() if s1 is s2: print('yeah')

【二】__str__,__repr__

【1】__str__

  • __str__方法会在对象被打印时自动触发
    • print功能打印的就是它的返回值
    • 我们通常基于方法来定制对象的打印信息
    • 该方法必须返回字符串类型
class People: def __init__(self, name, age): self.name = name self.age = age def __str__(self): # 返回类型必须是字符串 return f'<Name:{self.name} Age:{self.age}>' person = People('dream', 18) print(person) # 触发p.__str__(),拿到返回值后进行打印 # <Name:dream Age:18>

【2】__repr__

  • repr或者交互式解释器触发
class School: def __init__(self, name, addr, type): self.name = name self.addr = addr self.type = type def __repr__(self): return 'School(%s,%s)' % (self.name, self.addr) def __str__(self): return '(%s,%s)' % (self.name, self.addr) s1 = School('dreamCity', '北京', '私立') print('from repr: ', repr(s1)) print('from str: ', str(s1)) print(s1)

【3】小结

  • str函数或者print函数---> obj.__str__()
  • repr或者交互式解释器---> obj.__repr__()
  • 如果__str__没有被定义,那么就会使用__repr__来代替输出
  • 注意:这俩方法的返回值必须是字符串,否则抛出异常

【三】__del__方法

  • __del__会在对象被删除时自动触发。

  • 由于Python自带的垃圾回收机制会自动清理Python程序的资源,所以当一个对象只占用应用程序级资源时,完全没必要为对象定制__del__方法

  • 但在产生一个对象的同时涉及到申请系统资源(比如系统打开的文件、网络连接等)的情况下

    • 关于系统资源的回收,Python的垃圾回收机制便派不上用场了
    • 需要我们为对象定制该方法,用来在对象被删除时自动触发回收系统资源的操作
class MySQL: def __init__(self, ip, port): # 伪代码,发起网络连接,需要占用系统资源 self.conn = connect(ip, port) def __del__(self): # 关闭网络连接,回收系统资源 self.conn.close() # 在对象obj被删除时,自动触发obj.__del__() obj = MySQL('127.0.0.1', 3306)
class Person(): def __init__(self, name, age): print(f'这是 :>>>> {name} , age :>>>> {age}') def __del__(self): print(f"我离开你了") person = Person("dream", 18) print("这是断层")

【四】isinstance(obj,cls)issubclass(sub,super)

  • isinstance(obj,cls)检查是否obj是否是类cls 的对象
class Bar(): pass class Foo(object): pass foo = Foo() bar = Bar() # isinstance(obj,cls)检查是否obj是否是类cls的对象 res_Foo = isinstance(foo, Foo) res_Bar = isinstance(foo, Bar) print(res_Foo) print(res_Bar)
  • issubclass(sub, super)检查sub类是否是 super 类的派生类
class Foo(object): pass class Bar(Foo): pass res = issubclass(Bar, Foo) print(res)

【五】__doc__

class Foo: '我是描述信息' pass print(Foo.__doc__)
class Foo: '我是描述信息' pass class Bar(Foo): pass # 该属性无法继承给子类 print(Bar.__doc__)

【六】__enter____exit__

# 我们知道在操作文件对象的时候可以这么写 with open('a.txt') as f:   '代码块'
  • 上述叫做上下文管理协议,即with语句
  • 为了让一个对象兼容with语句,必须在这个对象的类中声明__enter____exit__方法
class Open: def __init__(self, name): self.name = name def __enter__(self): print('出现with语句,对象的__enter__被触发,有返回值则赋值给as声明的变量') # return self def __exit__(self, exc_type, exc_val, exc_tb): print('with中代码块执行完毕时执行我啊') with Open('a.txt') as f: print('=====>执行代码块') # print(f,f.name)
  • __exit__()中的三个参数分别代表

    • 异常类型

    • 异常值

    • 追溯信息

  • with语句中代码块出现异常,则with后的代码都无法执行

class Open: def __init__(self, name): self.name = name def __enter__(self): print('出现with语句,对象的__enter__被触发,有返回值则赋值给as声明的变量') def __exit__(self, exc_type, exc_val, exc_tb): print('with中代码块执行完毕时执行我啊') print(exc_type) print(exc_val) print(exc_tb) with Open('a.txt') as f: print('=====>执行代码块') raise AttributeError('***着火啦,救火啊***') print('0' * 100) # ------------------------------->不会执行
  • 如果__exit__()返回值为True,那么异常会被清空,就好像啥都没发生一样
    • with后的语句正常执行
class Open: def __init__(self, name): self.name = name def __enter__(self): print('出现with语句,对象的__enter__被触发,有返回值则赋值给as声明的变量') def __exit__(self, exc_type, exc_val, exc_tb): print('with中代码块执行完毕时执行我啊') print(exc_type) print(exc_val) print(exc_tb) return True with Open('a.txt') as f: print('=====>执行代码块') raise AttributeError('***着火啦,救火啊***') print('0' * 100) # ------------------------------->会执行 # 出现with语句,对象的__enter__被触发,有返回值则赋值给as声明的变量 # =====>执行代码块 # with中代码块执行完毕时执行我啊 # <class 'AttributeError'> # ***着火啦,救火啊*** # <traceback object at 0x000002591C0AC140> # 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000

【七】__setattr__,__delattr__,__getattr__

【1】方法介绍

  • __getattr__(self, name): 访问不存在的属性时调用

  • __setattr__(self, name, value):设置实例对象的一个新的属性时调用

  • __delattr__(self, name):删除一个实例对象的属性时调用

class Foo: x = 1 def __init__(self, y): self.y = y def __getattr__(self, item): print(f'----> from getattr:你找的属性不存在 :>>>> {item}') def __setattr__(self, key, value): print(f'----> from setattr key : {key} value : {value}') # self.key=value #这就无限递归了,你好好想想 # self.__dict__[key]=value #应该使用它 def __delattr__(self, item): print(f'----> from delattr :>>>> {item}') # del self.item #无限递归了 self.__dict__.pop(item) # __setattr__添加/修改属性会触发它的执行 f1 = Foo(10) # 因为你重写了__setattr__,凡是赋值操作都会触发它的运行,你啥都没写,就是根本没赋值,除非你直接操作属性字典,否则永远无法赋值 print(f1.__dict__) f1.z = 3 print(f1.__dict__) # __delattr__删除属性的时候会触发 f1.__dict__['a'] = 3 # 我们可以直接修改属性字典,来完成添加/修改属性的操作 del f1.a print(f1.__dict__) # __getattr__只有在使用点调用属性且属性不存在的时候才会触发 f1.xxxxxx
  • 对象属性查找顺序
    • 首先访问__getattribute__()魔法方法(隐含默认调用,无论何种情况,均会调用此方法
    • 去实例对象t中查找是否具备该属性: t.__dict__ 中查找,每个类和实例对象都有一个__dict__的属性
    • 若在t.__dict__中找不到对应的属性, 则去该实例的类中寻找,即 t.__class__.__dict__
    • 若在实例的类中也招不到该属性,则去父类中寻找,即 t.__class__.__bases__.__dict__中寻找
    • 若以上均无法找到,则会调用__getattr__方法,执行内部的命令(若未重载 __getattr__ 方法,则直接报错:AttributeError)

【2】二次加工标准类型(包装)

(1)包装

  • 包装:python为大家提供了标准数据类型,以及丰富的内置方法,其实在很多场景下我们都需要基于标准数据类型来定制我们自己的数据类型,新增/改写方法,这就用到了我们刚学的继承/派生知识(其他的标准类型均可以通过下面的方式进行二次加工)
# 继承list所有的属性,也可以派生出自己新的,比如append和mid class List(list): def append(self, p_object): ' 派生自己的append:加上类型检查' if not isinstance(p_object, int): raise TypeError('must be int') super().append(p_object) @property def mid(self): '新增自己的属性' index = len(self) // 2 return self[index] l = List([1, 2, 3, 4]) print(l) l.append(5) print(l) # l.append('1111111') #报错,必须为int类型 print(l.mid) # 其余的方法都继承list的 l.insert(0, -123) print(l) l.clear() print(l)

(2)授权

  • 授权:授权是包装的一个特性, 包装一个类型通常是对已存在的类型的一些定制,这种做法可以新建,修改或删除原有产品的功能。其它的则保持原样。

  • 授权的过程,即是所有更新的功能都是由新类的某部分来处理,但已存在的功能就授权给对象的默认属性。

  • 实现授权的关键点就是覆盖__getattr__方法

import time class FileHandle: def __init__(self, filename, mode='r', encoding='utf-8'): # 定义一个文件句柄 ---- 文件打开 并 将文件句柄给 self.file self.file = open(filename, mode, encoding=encoding) def write(self, line): t = time.strftime('%Y-%m-%d %T') self.file.write('%s %s' % (t, line)) def __getattr__(self, item): # 返回 映射方法 ---- 映射自己self.file 是否存在 read 方法 # print(item) # read return getattr(self.file, item) # 实例化类得到对象 f1 = FileHandle('b.txt', 'w+') # 写入文本内容 f1.write('你好啊') # 移动光标 f1.seek(0) # 触发 __getattr__(self, item) 方法 , self 是 f1 对象 ,item 是 read 方法 print(f1.read()) # 关闭 对象 f1.close()
# 自定义文件处理器 class FileHandle: def __init__(self, filename, mode='r', encoding='utf-8'): if 'b' in mode: self.file = open(filename, mode) else: self.file = open(filename, mode, encoding=encoding) self.filename = filename self.mode = mode self.encoding = encoding def write(self, line): if 'b' in self.mode: if not isinstance(line, bytes): raise TypeError('must be bytes') self.file.write(line) def __getattr__(self, item): return getattr(self.file, item) def __str__(self): if 'b' in self.mode: res = "<_io.BufferedReader name='%s'>" % self.filename else: res = "<_io.TextIOWrapper name='%s' mode='%s' encoding='%s'>" % (self.filename, self.mode, self.encoding) return res f1 = FileHandle('b.txt', 'wb') # f1.write('你好啊啊啊啊啊') # #自定制的write,不用在进行encode转成二进制去写了,简单,大气 f1.write('你好啊'.encode('utf-8')) print(f1) f1.close()

【八】__setitem__,__getitem__,__delitem__

  • __getitem__()将对象当作list使用
  • 如obj = ACLASS(), obj_first=obj[0]
class Foo: def __init__(self, name): self.name = name def __getitem__(self, item): print(self.__dict__[item]) def __setitem__(self, key, value): self.__dict__[key] = value def __delitem__(self, key): print('del obj[key]时,我执行') self.__dict__.pop(key) def __delattr__(self, item): print('del obj.key时,我执行') self.__dict__.pop(item) f1 = Foo('sb') f1['age'] = 18 f1['age1'] = 19 del f1.age1 del f1['age'] f1['name'] = 'alex' print(f1.__dict__)

【九】__call__

  • 对象后面加括号,触发执行。

  • 注:构造方法的执行是由创建对象触发的

    • 即:对象 = 类名() ;
  • 而对于 __call__ 方法的执行是由对象后加括号触发的

    • 即:对象() 或者 类()()
class Foo: def __init__(self): pass def __call__(self, *args, **kwargs): print('__call__') obj = Foo() # 执行 __init__ obj() # 执行 __call__

【十】__gt__(),__lt__(),__eq__(),__ne__(),__ge__()

  • 定义对象比较方法,为关系运算符>,>=,<,==等提供接口
class SavingsAccount(object): def __init__(self, name, pin, balance=0.0): self._name = name self._pin = pin self._balance = balance def __lt__(self, other): return self._balance < other._balance s1 = SavingsAccount("dream", "1000", 0) s2 = SavingsAccount("hope", "1001", 30) print(s1 < s2)
  • 其实,类似的数值运算的魔法方法非常非常之多,好吧,我真的用了两个,现在是三个非常来形容,以下列出部分
class Point(object): def __init__(self): self.x = -1 self.y = 5 # 正负,取反、绝对值 def __pos__(self): pass def __neg__(self): pass def __invert__(self): pass def __abs__(self): pass # 加减乘除、取余,指数 def __add__(self, other): pass def __sub__(self, other): pass def __divmod__(self, other): pass def __mul__(self, other): pass def __mod__(self, other): pass def __pow__(self, power, modulo=None): pass # 逻辑运算符 def __and__(self, other): pass def __or__(self, other): pass def __xor__(self, other): pass # 位移 def __lshift__(self, other): pass def __rshift__(self, other): pass # 赋值语句 def __iadd__(self, other): pass def __imul__(self, other): pass def __isub__(self, other): pass def __idiv__(self, other): pass def __imod__(self, other): pass def __ipow__(self, other): pass def __ilshift__(self, other): pass def __irshift__(self, other): pass

【十一】__iter__()

  • 为for … in提供接口,返回一个迭代对象,并调用对象next()方法,直到StopIteration
class Fib(object): def __init__(self): self.a, self.b = 0, 1 # 初始化两个计数器a,b def __iter__(self): return self # 实例本身就是迭代对象,故返回自己 def __next__(self): self.a, self.b = self.b, self.a + self.b # 计算下一个值 if self.a > 10: # 退出循环的条件 raise StopIteration() return self.a # 返回下一个值 for i in Fib(): print(i)

【十二】描述符(__get__(),__set__(),__delete__())

【1】什么是描述符

  • 描述符本质就是一个新式类,在这个新式类中
    • 至少实现了__get__(),__set__(),__delete__()中的一个
    • 这也被称为描述符协议
  • __get__():调用一个属性时,触发
  • __set__():为一个属性赋值时,触发
  • __delete__():采用del删除属性时,触发
class Foo: #在python3中Foo是新式类,它实现了三种方法,这个类就被称作一个描述符 def __get__(self, instance, owner): pass def __set__(self, instance, value): pass def __delete__(self, instance): pass

【2】描述符的作用

  • 描述符的作用是用来代理另外一个类的属性的(必须把描述符定义成这个类的类属性,不能定义到构造函数中)

【3】描述符分两种

  • 数据描述符:至少实现了__get__()__set__()
class Foo: def __set__(self, instance, value): print('set') def __get__(self, instance, owner): print('get')

__EOF__

本文作者Chimengmeng
本文链接https://www.cnblogs.com/dream-ze/p/17867258.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   Chimengmeng  阅读(93)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示