python外篇(魔术方法)
目录
归类
new和init
str和repr
是否需要实现
自实现with open
具体的官方文档学习链接放前面: https://docs.python.org/3/reference/datamodel.html#special-method-names
归类
### 1. 对象表示相关的魔术方法:
__str__(self):返回对象的字符串表示形式,用于将对象转化为人类可读的字符串形式。
__repr__(self):返回对象的可打印的字符串形式,用于调试和开发,通常需要满足eval(repr(obj)) == obj。
__format__(self, format_spec):指定对象在使用format函数进行格式化时的字符串形式。
### 2. 对象构造和初始化相关的魔术方法:
__new__(cls, *args, **kwargs):用于创建一个新的对象,通常与类方法一起使用。
__init__(self, *args, **kwargs):用于初始化对象的属性,通常用于初始化对象的状态和行为。
__del__(self):用于对象的删除和垃圾回收,通常用于释放对象占用的资源。
### 3. 对象比较和排序相关的魔术方法:
__eq__(self, other):用于判断两个对象是否相等。
__ne__(self, other):用于判断两个对象是否不相等。
__lt__(self, other):用于判断一个对象是否小于另一个对象。
__le__(self, other):用于判断一个对象是否小于或等于另一个对象。
__gt__(self, other):用于判断一个对象是否大于另一个对象。
__ge__(self, other):用于判断一个对象是否大于或等于另一个对象。
### 4. 对象属性访问相关的魔术方法:
__getattribute__(self, name):用于获取对象的不存在的属性时的行为,__getattr__作为备用。
__setattr__(self, name, value):用于设置对象属性的值时的行为。
__delattr__(self, name):用于删除对象属性时的行为。
### 5. 对象容器和迭代相关的魔术方法:
__len__(self):用于返回对象的长度。
__getitem__(self, key):用于获取对象的元素。
__setitem__(self, key, value):用于设置对象的元素。
__delitem__(self, key):用于删除对象的元素。
__iter__(self):用于获取迭代器对象。
__next__(self):用于获取下一个迭代器对象。
### 6. 对象运算符重载相关的魔术方法:
__add__(self, other):用于实现加法运算。
__sub__(self, other):用于实现减法运算。
__mul__(self, other):用于实现乘法运算。
__truediv__(self, other):用于实现真除法运算。
__floordiv__(self, other):用于实现地板除法运算。
__mod__(self, other):用于实现取模运算。
__pow__(self, other):用于实现幂运算。
__and__(self, other):用于实现按位与运算。
__or__(self, other):用于实现按位或运算。
__xor__(self, other):用于实现按位异或运算。
__neg__(self):用于实现负号运算。
__pos__(self):用于实现正号运算。
__abs__(self):用于实现绝对值运算。
__invert__(self):用于实现按位取反运算。
### 7. 上下文管理相关的魔术方法:
__enter__(self):用于进入上下文管理器时执行的操作。
__exit__(self, exc_type, exc_val, exc_tb):用于退出上下文管理器时执行的操作。
### 8. 其他魔术方法:
__call__(self, *args, **kwargs):用于使对象可以像函数一样调用。
__hash__(self):用于获取对象的哈希值。
__bool__(self):用于判断对象的真假值。
__instancecheck__(self, instance):用于检查对象是否是某个类的实例。
__subclasscheck__(self, subclass):用于检查对象是否是某个类的子类。
需要注意的是,不是所有的魔术方法都需要在自定义类中实现。根据业务需要实现所需的魔术方法即可。
new和init
### 简要:
(1) 先执行__new__,随后执行__init__
(2) new接受类对象,而init接受实例对象,即__new__(cls)、__init__(self)
(3) new用于创建并返回实例对象,而init用于接受实例对象并进行初始化
### 详细:
当我们创建一个类的实例对象时,首先会调用__new__()方法来创建一个实例对象,然后再调用__init__()方法来初始化这个实例对象。具体来说,__new__()方法用于创建一个空的实例对象,而__init__()方法则用于给这个实例对象添加属性和初始化值。
换句话说,__new__()方法是在实例对象被创建之前调用的,而__init__()方法则是在实例对象被创建之后调用的。__new__()方法返回一个实例对象,这个实例对象会被传递给__init__()方法作为第一个参数(即self参数),然后在__init__()方法中对这个实例对象进行初始化操作。
需要注意的是,__new__()方法的第一个参数是类对象(即cls参数),而__init__()方法的第一个参数是实例对象(即self参数)。另外,如果__new__()方法没有返回一个实例对象,则__init__()方法不会被调用。### 代码示例:
class MyClass: # 1. __new__ def __new__(cls, *args, **kwargs): # 1.1 创建实例 instance = object.__new__(cls) # 1.2 返回实例 return instance # 2. __init__ def __init__(self,name,age): # 2.1 实例初始化 self.name = name self.age = age obj = MyClass('joden',20) # new --> init
str和repr
### 简要
(1) __str__和__repr__都接受对象实例即self
(2) __str__用于定义对象的字符串表达形式,常用于在用户界面或日志输出等场景
(3) __repr__用于定义对象的“代码”表示形式,常用于代码调试
(4) 如果__str__不存在,将会用__repr__来替代__str__,repr更像是一个备用
### 详细:
__str__()用于定义对象的字符串表示,_repr__()用于定义对象的“代码”表示形式。
__str__()方法用于定义对象的字符串表示形式,即在使用print()函数或者str()函数将对象转换为字符串时,会调用该方法并返回一个字符串对象。如果一个对象没有定义__str__()方法,那么会调用__repr__()方法来代替。
__repr__()方法用于定义对象的“代码”表示形式,即在使用内置repr()函数将对象转换为字符串时,会调用该方法并返回一个字符串对象。这个字符串对象应当是一个合法的 Python 表达式,通过执行该表达式可以重新得到该对象。如果一个对象没有定义__repr__()方法,那么会默认返回一个类似<__main__.ClassName object at 0x7f50d6199ac0>的字符串。
注意,str和repr都与type没有任何关系。
### 代码示例:
class Person: def __init__(self, name, age): self.name = name self.age = age def __str__(self): print('__str__被调用') return f"{self.name} is {self.age} years old." def __repr__(self): print('__repr__被调用') return "Person({},{})".format(self.name,self.age) p = Person("John", 25) # 调用print函数 print(p) # 输出:John is 25 years old. print(repr(p)) # 输出:Person(John,25)
是否需要实现
### 前言
首先要明确自定的实现类都默认继承了最底层的Object类,所以也就继承了Object类中所有的魔术方法。
在魔术方法中有些需要我们手动编码去实现以便满足我们的应用场景,当然也有些魔术方法一般不需要修改即是符合正常逻辑的,所以了解Object类中魔术方法的默认行为很重要。
下面可以根据需求将魔术分为:需要实现与不需要实现 (当然这是一般情况)
不同python版本的魔术方法也有一些不同,但是我们可以使用__dir__()打印出来看一下。
### 打印魔术方法(如下)
(1) 打印类的魔术方法(如下)
print(Person.__dir__(Person))
'__repr__', '__call__', '__getattribute__', '__setattr__', '__delattr__', '__init__', '__new__', '__subclasses__', '__prepare__', '__instancecheck__', '__subclasscheck__', '__dir__', '__sizeof__', '__basicsize__', '__itemsize__', '__flags__', '__weakrefoffset__', '__base__', '__dictoffset__', '__mro__', '__name__', '__qualname__', '__bases__', '__module__', '__abstractmethods__', '__dict__', '__doc__', '__text_signature__', '__hash__', '__str__', '__lt__', '__le__', '__eq__', '__ne__', '__gt__', '__ge__', '__reduce_ex__', '__reduce__', '__subclasshook__', '__init_subclass__', '__format__', '__class__'
可以看到,有__getattribute__而不是__getattr__(备用)
(2) 打印对象的魔术方法(如下)
print(p1.__dir__())
'__module__', '__init__', '__str__', '__repr__', '__dict__', '__doc__', '__hash__', '__getattribute__', '__setattr__', '__delattr__', '__lt__', '__le__', '__eq__', '__ne__', '__gt__', '__ge__', '__new__', '__reduce_ex__', '__reduce__', '__subclasshook__', '__init_subclass__', '__format__', '__sizeof__', '__dir__', '__class__'
可以看到对比类中的少了很多,这些都是内部自动从对象中已经继承的魔术方法
### 类中好用的魔术方法
__bases__:返回一个tuple包含了当前类的所有直接父类(一层)
__base__:返回一个class为当前类的首个直接父类(一层首个)
__subclasses__():返回一个list包含了当前类的所有直接子类(一层)
__mro__:返回一个tuple包含了当前类的所有继承关系类(多层)
mro():返回一个list包含了当前类的所有继承关系类(多层)
__dict__:返回一个字典包含了当前类中所有的静态属性、实例属性、方法
### 不需要实现:
__dir__(self):获取对象的属性和方法的列表,默认即可
__doc__(self):获取类中的文档字符串(三引号包裹的字符串)且不存在时返回None,默认即可
__class__(self):获取对象的类,默认即可
__module__(self):获取类定义所在默认的模块名称,默认即可
__dict__:获取实例对象的属性字典(不包含方法),默认即可
__reduce__:对象序列化和反序列化被调用的方法,默认即可
__new__(cls):创建实例并返回嘛又也不是初始化,默认即可
__del__(self):销毁实例,垃圾回收机制已经帮我们做了,默认即可
__getattribute__(self,name):获取属性且不存在时抛出AttributeError,一般默认
__setattr__(self,name,value):为对象设置绑定的属性,默认即可
__delattr__(self,name):删除属性不存在时抛出AttributeError,默认即可
__sizeof__(self):获取对象占用的内存字节数,默认即可
__format__(self,format_spec):获取对象的格式化字符串表示会抛出TypeError,默认即可
### 需要实现:
__init__(self,*args,**kwargs):实例化创建对象,需要去实现
__str__(self):对象的字符串表现形式,需要我们去实现
__repr__(self):对象的“代码”表现形式,需要我们去实现
__call__(self):回调方法,默认会在对象被调用的时候执行(即在init方法之后执行),需要我们去显式的定义
__eq__(self,obj):对象比较时调用,默认比较内存id是否相等于,需要实现
__ne__(self,obj):对象比较时调用,默认比较内存id是否不相等于,需要实现
__lt__(self,obj):对象比较时调用,默认比较内存id是否小于,需要实现
__le__(self,obj):对象比较时调用,默认比较内存id是否小于等于,需要实现
__gt__(self,obj):对象比较时调用,默认比较内存id是否大于,需要实现
__ge__(self,obj):对象比较时调用,默认比较内存id是否大于等于,需要实现
__enter__(self):使用with语法时就会调用它,并且__enter__(self)的返回值会被赋值给as后面的变量,可用于实现上下文管理类(像我们使用的with open就是该原理)
__exit(self,exc_type,exc_value,traceback):和__enter__(self)结合使用,当with下的代码块被中完成后__exit__()将会被执行,通常我们在这里进行释放那个资源、清理环境等操作(像我们使用的with open就是该原理)
自实现with open
class MyOpen:
def __init__(self,filename,mode):
self.filename = filename
self.mode = mode
def __enter__(self):
# 在这里创建一个文件流
self.file = open(self.filename,self.mode)
return self.file
def __exit__(exc_type,exc_value,exc_tb):
# 在这里把文件流关闭
self.file.close()
with MyOpen('test.txt','w') as f: # with会自动执行__enter__,并且有 f = self.file
f.write("Hello Joden") # 执行完会自动执行__exit__