Python面向对象之内置方法
3311 字
17 分钟
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 = 3print(f1.__dict__)
# __delattr__删除属性的时候会触发f1.__dict__['a'] = 3 # 我们可以直接修改属性字典,来完成添加/修改属性的操作del f1.aprint(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和midclass 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'] = 18f1['age1'] = 19del f1.age1del 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删除属性时>采用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')支持与分享
如果这篇文章对你有帮助,欢迎分享给更多人或打赏支持!
相关文章智能推荐
1
C 语言发展史
编程语言了解 C 语言从 BCPL、B 语言到现代标准 C 的发展历程、核心特点与应用领域。
2
C 语言开发环境的安装与配置
编程语言在 Windows、Linux 与 macOS 上安装 C 工具链,配置编辑器,并编译运行第一个 C 程序。
3
C 语言初识:程序运行机制与基本结构
编程语言认识 C 程序从源代码到运行的完整流程,以及基础语法和程序的基本结构。
4
C 语言基础:注释、关键字、变量与数据类型
编程语言学习 C 语言的注释、关键字、标识符、常量、变量和基本数据类型等基础知识。
5
C 语言数据类型
编程语言理解 C 语言的数据类型体系,包括基本类型、整数类型、浮点类型和逻辑类型。
随机文章随机推荐














