1. 实例创建和销毁
__new__
是一个静态方法,它会将所请求实例所属的类作为第一个参数,并返回一个cls
类的新实例,是类级别的方法。允许不可变类型的子类(例如int
,str
,tuple
)定制实例创建的过程,常会在自定义元类中被重载,以实现定制类的创建过程。__init__
在实例被创建之后,返回调用者之前调用。对象是由__new__
创建,并由__init__
进行定制,进行初始化,所以其返回值只能是None
,否则会在运行时引发TypeError
。
通常用于初始化一个新实例,控制这个初始化的过程,比如添加一些属性, 做一些额外的操作,发生在类实例被创建完以后,是实例级别的方法。__del__
对象被内存中销毁前,会被自动调用。del x
并不直接调用x.__del__()
,前者会将x
的引用计数减1,而后者仅会在x
的引用计数变为0时被调用。
# 示例代码
class Cat:
def __new__(cls, *args, **kwargs):
print('__new__被调用了')
return super().__new__(cls)
def __init__(self, name):
print('__init__被调用了')
self.name = name
def __del__(self):
print('__del__被调用了')
if __name__ == '__main__':
tom = Cat(name='Tom')
jack = tom
print(tom)
del tom # 当引用次数不为0时,不会调用__del__
print('-'*50)
print(jack)
# 示例代码
class Cat:
def __new__(cls, *args, **kwargs):
print('__new__被调用了')
return super().__new__(cls)
def __init__(self, name):
print('__init__被调用了')
self.name = name
def __del__(self):
print('__del__被调用了')
if __name__ == '__main__':
tom = Cat(name='Tom')
jack = tom
print(tom)
del tom # 当引用次数不为0时,不会调用__del__
print('-'*50)
print(jack)
# 输出结果
__new__被调用了
__init__被调用了
<__main__.cat>0x0000018A72B48B80>
--------------------------------------------------<__main__.cat>0x0000018A72B48B80>
__del__被调用了
# 输出结果
__new__被调用了
__init__被调用了
<__main__.cat>0x0000018A72B48B80>
--------------------------------------------------<__main__.cat>0x0000018A72B48B80>
__del__被调用了
2. 集合模拟
__len__
调用此方法以实现内置函数len()
。应该返回对象的长度,以一个>=0
的整数表示。__getitem__
调用此方法以实现self[key]
的求值。对于序列类型,接受的键应为整数和切片对象。请注意负数索引(如果类想要模拟序列类型)的特殊解读是取决于__getitem__()
方法。__setitem__
调用此方法以实现向self[key]
赋值。为对象实现此方法应该仅限于需要映射允许基于键修改值或添加键,或是序列允许元素被替换时。__delitem__
调用此方法以实现self[key]
的删除。为对象实现此方法应该权限于需要映射允许移除键,或是序列允许移除元素时。__contains__
调用此方法以实现成员检测运算符。如果item
是self
的成员则应返回真,否则返回假。对于映射类型,此检测应基于映射的键而不是值或者键值对。
# 示例代码
class CountList:
def __init__(self, *args):
self.values = [x for x in args]
def __len__(self):
# 调用`len`方法会触发
return len(self.values)
def __getitem__(self, item):
# 调用`self[key]`进行取值会触发
return self.values[item]
def __setitem__(self, key, value):
# 调用`self[key]`进行赋值会触发
self.values[key] = value
def __delitem__(self, key):
# 调用`del`会触发
self.values.pop(key)
def __contains__(self, item):
# 调用`in`会触发
return item in self.values
if __name__ == '__main__':
count_list = CountList(1, 2, 3, 4, 5, 6, 7)
print("列表为:".format(count_list.values))
print("列表长度为:{}".format(len(count_list)))
print("列表中第3个元素为:{}".format(count_list[2]))
print("-----修改第3个元素的值-----")
count_list[2] = 10
print("修改列表中第3个元素后为:{}".format(count_list.values))
print("-----删除第3个元素的值-----")
del count_list[2]
print("删除列表中第3个元素后为:{}".format(count_list.values))
print("-----判断3是否在列表中-----")
print("3在列表中:{}".format(3 in count_list))
print("4在列表中:{}".format(4 in count_list))
# 示例代码
class CountList:
def __init__(self, *args):
self.values = [x for x in args]
def __len__(self):
# 调用`len`方法会触发
return len(self.values)
def __getitem__(self, item):
# 调用`self[key]`进行取值会触发
return self.values[item]
def __setitem__(self, key, value):
# 调用`self[key]`进行赋值会触发
self.values[key] = value
def __delitem__(self, key):
# 调用`del`会触发
self.values.pop(key)
def __contains__(self, item):
# 调用`in`会触发
return item in self.values
if __name__ == '__main__':
count_list = CountList(1, 2, 3, 4, 5, 6, 7)
print("列表为:".format(count_list.values))
print("列表长度为:{}".format(len(count_list)))
print("列表中第3个元素为:{}".format(count_list[2]))
print("-----修改第3个元素的值-----")
count_list[2] = 10
print("修改列表中第3个元素后为:{}".format(count_list.values))
print("-----删除第3个元素的值-----")
del count_list[2]
print("删除列表中第3个元素后为:{}".format(count_list.values))
print("-----判断3是否在列表中-----")
print("3在列表中:{}".format(3 in count_list))
print("4在列表中:{}".format(4 in count_list))
# 输出结果
列表为:[1, 2, 3, 4, 5, 6, 7]
列表长度为:7
列表中第3个元素为:3
-----修改第3个元素的值-----
修改列表中第3个元素后为:[1, 2, 10, 4, 5, 6, 7]
-----删除第3个元素的值-----
删除列表中第3个元素后为:[1, 2, 4, 5, 6, 7]
-----判断3是否在列表中-----
3在列表中:False
4在列表中:True
# 输出结果
列表为:[1, 2, 3, 4, 5, 6, 7]
列表长度为:7
列表中第3个元素为:3
-----修改第3个元素的值-----
修改列表中第3个元素后为:[1, 2, 10, 4, 5, 6, 7]
-----删除第3个元素的值-----
删除列表中第3个元素后为:[1, 2, 4, 5, 6, 7]
-----判断3是否在列表中-----
3在列表中:False
4在列表中:True
3.字符串 /字节序列表示形式
__str__
通过str(object)
以及内置函数format()
和print()
调用以生成一个对象的“非正式”或格式良好的字符串表示。返回值必须为一个 字符串 对象。类的实例打印时调用这个函数返回用户看到的__repr__
由repr()
内置函数调用以输出一个对象的“官方”字符串表示,返回值必须是一个字符串对象,返回开发者看到的。当__str__
没有实现时,进行打印或者转换字符串,会调用__repr__
。__format__
通过format()
内置函数、扩展、格式化字符串字面值 的求值以及str.format()
方法调用以生成一个对象的“格式化”字符串表示。format_spec
参数为包含所需格式选项描述的字符串。format_spec
参数的解读是由实现__format__()
的类型决定的,不过大多数类或是将格式化委托给某个内置类型,或是使用相似的格式化选项语法。__bytes__
通过 bytes 调用以生成一个对象的字节串表示。这应该返回一个bytes
对象。
# 示例代码
class Cat:
def __init__(self, name, age):
self.name = name
self.age = age
def __str__(self):
# 调用`print`,`str`会触发
print("__str__被调用")
return "Cat(name=" + str(self.name) + ",age=" + str(self.age) + ")"
def __repr__(self):
# 调用'repr'时会触发,当`__str__`没有实现,调用`print`,`str`会触发
print("__repr__被调用")
return self.name
def __format__(self, format_spec):
# 调用 `.format(self)`,`format`时会触发
print("__format__被调用")
if format_spec == '':
return self.name
return format_spec.replace("%s", self.name).replace("%r", self.age)
def __bytes__(self):
# 调用`bytes`时会触发
print("__bytes__被调用")
return bytes(self.name, encoding='utf-8')
if __name__ == '__main__':
tom = Cat(name="Tom", age="18")
print("-"*50)
print(tom)
str(tom)
print("-"*50)
repr(tom)
print("-" * 50)
"{}".format(tom)
format(tom)
print("-" * 50)
bytes(tom)
# 示例代码
class Cat:
def __init__(self, name, age):
self.name = name
self.age = age
def __str__(self):
# 调用`print`,`str`会触发
print("__str__被调用")
return "Cat(name=" + str(self.name) + ",age=" + str(self.age) + ")"
def __repr__(self):
# 调用'repr'时会触发,当`__str__`没有实现,调用`print`,`str`会触发
print("__repr__被调用")
return self.name
def __format__(self, format_spec):
# 调用 `.format(self)`,`format`时会触发
print("__format__被调用")
if format_spec == '':
return self.name
return format_spec.replace("%s", self.name).replace("%r", self.age)
def __bytes__(self):
# 调用`bytes`时会触发
print("__bytes__被调用")
return bytes(self.name, encoding='utf-8')
if __name__ == '__main__':
tom = Cat(name="Tom", age="18")
print("-"*50)
print(tom)
str(tom)
print("-"*50)
repr(tom)
print("-" * 50)
"{}".format(tom)
format(tom)
print("-" * 50)
bytes(tom)
# 输出结果
--------------------------------------------------
__str__被调用
Cat(name=Tom,age=18)
__str__被调用
--------------------------------------------------
__repr__被调用
--------------------------------------------------
__format__被调用
__format__被调用
--------------------------------------------------
__bytes__被调用
# 输出结果
--------------------------------------------------
__str__被调用
Cat(name=Tom,age=18)
__str__被调用
--------------------------------------------------
__repr__被调用
--------------------------------------------------
__format__被调用
__format__被调用
--------------------------------------------------
__bytes__被调用
4.数值转换
__abs__
调用此方法以实现内置函数abs()
。__bool__
调用此方法以实现真值检测以及内置的bool()
操作;应该返回False
或True
。如果未定义此方法,则会查找并调用__len__()
并在其返回非零值时视对象的逻辑值为真。如果一个类既未定义__len__()
也未定义__bool__()
则视其所有实例的逻辑值为真。__complex__
调用此方法以实现内置函数complex()
,应当返回一个complex
类型的值。__int__
调用此方法以实现内置函数int()
,应当返回一个int
类型的值。__float__
调用此方法以实现内置函数float()
,应当返回一个float
类型的值。__hash__
通过内置函数hash()
调用以对哈希集的成员进行操作,属于哈希集的类型包括set
、frozenset
以及dict
。__hash__()
应该返回一个整数。对象比较结果相同所需的唯一特征属性是其具有相同的哈希值;建议的做法是把参与比较的对象全部组件的哈希值混在一起,即将它们打包为一个元组并对该元组做哈希运算。__index__
调用此方法以实现operator.index()
以及 Python 需要无损地将数字对象转换为整数对象的场合(例如切片或是内置的bin()
,hex()
和oct()
函数)。存在此方法表明数字对象属于整数类型。必须返回一个整数。
如果未定义__int__()
,__float__()
和__complex__()
则相应的内置函数int()
,float()
和complex()
将回退为__index__()
。
# 示例代码
class Dog:
def __init__(self, name):
self.name = name
def __abs__(self):
# 调用`abs`时会触发
print("__abs__被调用了")
return '1'
def __bool__(self):
# 调用`bool`时会触发
print("__bool__被调用了")
return False
def __complex__(self):
# 调用`complex`时会触发
print("__complex__被调用了")
return complex(1)
def __int__(self):
# 调用`int`时会触发
print("__int__被调用了")
return 1
def __float__(self):
# 调用`float`时会触发
print("__float__被调用了")
return 1.0
def __index__(self):
# 调用`operator.index`时会触发
print("__index__被调用了")
return 1
def __hash__(self):
# 调用`hash`时会触发
print("__hash__被调用了")
return 1
if __name__ == "__main__":
dg = Dog("bob")
print("-"*50)
abs(dg)
print("-" * 50)
bool(dg)
print("-" * 50)
complex(dg)
print("-" * 50)
int(dg)
print("-" * 50)
float(dg)
print("-" * 50)
import operator
operator.index(dg)
print("-" * 50)
hash(dg)
# 示例代码
class Dog:
def __init__(self, name):
self.name = name
def __abs__(self):
# 调用`abs`时会触发
print("__abs__被调用了")
return '1'
def __bool__(self):
# 调用`bool`时会触发
print("__bool__被调用了")
return False
def __complex__(self):
# 调用`complex`时会触发
print("__complex__被调用了")
return complex(1)
def __int__(self):
# 调用`int`时会触发
print("__int__被调用了")
return 1
def __float__(self):
# 调用`float`时会触发
print("__float__被调用了")
return 1.0
def __index__(self):
# 调用`operator.index`时会触发
print("__index__被调用了")
return 1
def __hash__(self):
# 调用`hash`时会触发
print("__hash__被调用了")
return 1
if __name__ == "__main__":
dg = Dog("bob")
print("-"*50)
abs(dg)
print("-" * 50)
bool(dg)
print("-" * 50)
complex(dg)
print("-" * 50)
int(dg)
print("-" * 50)
float(dg)
print("-" * 50)
import operator
operator.index(dg)
print("-" * 50)
hash(dg)
# 输出结果
--------------------------------------------------
__abs__被调用了
--------------------------------------------------
__bool__被调用了
--------------------------------------------------
__complex__被调用了
--------------------------------------------------
__int__被调用了
--------------------------------------------------
__float__被调用了
--------------------------------------------------
__index__被调用了
--------------------------------------------------
__hash__被调用了
# 输出结果
--------------------------------------------------
__abs__被调用了
--------------------------------------------------
__bool__被调用了
--------------------------------------------------
__complex__被调用了
--------------------------------------------------
__int__被调用了
--------------------------------------------------
__float__被调用了
--------------------------------------------------
__index__被调用了
--------------------------------------------------
__hash__被调用了
5.迭代枚举
__iter__
调用此方法以实现内置函数iter()
。此方法在需要为容器创建迭代器时被调用,该返回一个新的迭代器对象,它能够逐个迭代容器中的所有对象。对于映射,它应该逐个迭代容器中的键。
迭代器对象也需要实现此方法,它们需要返回对象自身。__reversed__
此方法会被reversed()
内置函数调用以实现逆向迭代。它应当返回一个新的以逆序逐个迭代容器内所有对象的迭代器对象。
如果未提供__reversed__()
方法,则reversed()
内置函数将回退到使用序列协议 (__len__()
和__getitem__()
。支持序列协议的对象应当仅在能够提供比reversed()
所提供的实现更高效的实现时才提供__reversed__()
方法。__next__
调用此方法以实现内置函数next()
。可以通过next
函数访问这个对象的下一个元素了,并且在你不想继续有迭代的情况下抛出一个StopIteration
的异常。
iterable
和iterator
区别可迭代的(
iterable
)如果对象的
__iter__
方法能返回迭代器,这就是可迭代的对象。序列都是可迭代的对象;此外,实现__getitem__
方法的对象也是可迭代的对象。迭代器(
iterator
)实现了无参数方法
__next__
的对象;这个方法返回级数里的下一个元素,如果没有元素了就抛出StopIteration
异常。在Python
中,迭代器还实现了__iter__
方法,因此迭代器也是可迭代的对象。根据最初的设计模式,经典迭代器返回集合里的元素。生成器也是迭代器,不过更灵活。标准的迭代器接口有两个方法:
__next__
返回下一个可用的元素,如果没有元素了,抛出StopIteration
异常。__iter__
返回self
,以便在应该使用可迭代对象的地方使用迭代器,例如在for
循环中。
# 示例代码
class Person:
def __init__(self, name, age, sex):
self.name = name
self.age = age
self.sex = sex
def __iter__(self):
# 调用`iter`或者`for`循环遍历时会触发
print("__iter__被调用了")
return (x for x in(self.name, self.age, self.sex))
def __next__(self):
# 调用`next`时会触发
print("__next__被调用了")
return self.age
def __reversed__(self):
# 调用`reversed`时会触发
print("__reversed__被调用了")
return None
if __name__ == "__main__":
p = Person("张三", 18, '男')
print("-"*50)
iter(p)
print("-" * 50)
for i in p:
print(i)
print("-" * 50)
print(next(p))
print("-" * 50)
reversed(p)
# 示例代码
class Person:
def __init__(self, name, age, sex):
self.name = name
self.age = age
self.sex = sex
def __iter__(self):
# 调用`iter`或者`for`循环遍历时会触发
print("__iter__被调用了")
return (x for x in(self.name, self.age, self.sex))
def __next__(self):
# 调用`next`时会触发
print("__next__被调用了")
return self.age
def __reversed__(self):
# 调用`reversed`时会触发
print("__reversed__被调用了")
return None
if __name__ == "__main__":
p = Person("张三", 18, '男')
print("-"*50)
iter(p)
print("-" * 50)
for i in p:
print(i)
print("-" * 50)
print(next(p))
print("-" * 50)
reversed(p)
# 输出结果
--------------------------------------------------
__iter__被调用了
--------------------------------------------------
__iter__被调用了
张三
18
男
--------------------------------------------------
__next__被调用了
18
--------------------------------------------------
__reversed__被调用了
# 输出结果
--------------------------------------------------
__iter__被调用了
--------------------------------------------------
__iter__被调用了
张三
18
男
--------------------------------------------------
__next__被调用了
18
--------------------------------------------------
__reversed__被调用了
6.可调用模拟
__call__
如果类实现了这个方法,相当于把这个类型的对象当作函数来使用,相当于重载了括号运算符。__call__
方法把一个对象变成为可调用对象,即通过obj()
直接调用对象时,系统执行的是对象的__call__
方法。实现__call__
方法的对象,callable(obj)
返回为True
。
应用方面,可以通过__call__
语法糖,简化对象的调用;也可以用户实现__call__
方法的对象替代基于函数的装饰器,简化代码结构。
# 示例代码
class Cat:
def __init__(self, name):
self.name = name
def speak(self):
print( self.name + " miao~ maio~ miao~")
def __call__(self, *args, **kwargs):
print("__call__被调用了")
self.speak()
if __name__ == "__main__":
tom = Cat("Tom")
print(callable(tom))
print("-"*50)
tom()
# 示例代码
class Cat:
def __init__(self, name):
self.name = name
def speak(self):
print( self.name + " miao~ maio~ miao~")
def __call__(self, *args, **kwargs):
print("__call__被调用了")
self.speak()
if __name__ == "__main__":
tom = Cat("Tom")
print(callable(tom))
print("-"*50)
tom()
# 输出结果
True
--------------------------------------------------
__call__被调用了
Tom miao~ maio~ miao~
# 输出结果
True
--------------------------------------------------
__call__被调用了
Tom miao~ maio~ miao~
7.上下文管理
__enter__
进入与此对象相关的运行时上下文。with
语句将会绑定这个方法的返回值到as
子句中指定的目标,如果有的话。__exit__
退出关联到此对象的运行时上下文。各个参数描述了导致上下文退出的异常。如果上下文是无异常地退出的,三个参数都将为None
。
如果提供了异常,并且希望方法屏蔽此异常(即避免其被传播),则应当返回真值。否则的话,异常将在退出此方法时按正常流程处理。
__enter__
和__exit__
可以让对象通过with
关键字来进行使用,提供进入with
块前的初始化工作和退出with
块后的清理工作,常用于文件和数据库操作中。使用with语句的目的就是把代码块放入with中执行,with结束后,自动完成清理工作,无须手动干预
在需要管理一些资源比如文件,网络连接和锁的编程环境中,可以在__exit__中定制自动释放资源的机制,你无须再去关系这个问题,这将大有用处
# 示例代码
class OpenFile:
def __init__(self, filepath):
self.filepath = filepath
def __enter__(self):
print('__enter__被调用了')
return self
def __exit__(self, exc_type, exc_val, exc_tb):
print('__exit__被调用了')
return True # 返回True,可以屏蔽抛出的异常
def __str__(self):
return self.filepath
if __name__ == "__main__":
with OpenFile('a.txt') as f:
print(f)
f.write() # 没有此方法,会抛出异常
# 示例代码
class OpenFile:
def __init__(self, filepath):
self.filepath = filepath
def __enter__(self):
print('__enter__被调用了')
return self
def __exit__(self, exc_type, exc_val, exc_tb):
print('__exit__被调用了')
return True # 返回True,可以屏蔽抛出的异常
def __str__(self):
return self.filepath
if __name__ == "__main__":
with OpenFile('a.txt') as f:
print(f)
f.write() # 没有此方法,会抛出异常
# 输出结果
__enter__被调用了
a.txt
__exit__被调用了
# 输出结果
__enter__被调用了
a.txt
__exit__被调用了
8.属性管理
__getattr__
当访问对象中不存在的属性时,因引发AttributeError
异常而失败,系统会去调用对象的__getattr__
方法 。此方法应当返回找到的属性值或是引发一个AttributeError
异常。
如果属性是通过正常机制找到的,__getattr__()
就不会被调用。
注意:
在实现了
__getattr__
方法的类的内部,对对象属性进行赋值时需要特别注意,很容易引发__getattr__
的无限循环。在__init__
中,对象的所有自定义属性都没有初始化,此时如果对属性赋值,会触发调用__setattr__
方法,访问属性则触发__getattr__
。所以在类内部,最好通过self.__dict__
对对象属性进行赋值和取值,避免通过点.
运算符(self.attr
和self.attr = xxx
)引发无限循环。
__getattribute__
一个属性访问拦截器,会拦截所有对对象属性访问请求,不管属性是否存在,且具有最优先的访问查询顺序。
此方法会无条件地被调用以实现对类实例属性的访问。如果类还定义了__getattr__()
,则后者不会被调用,除非__getattribute__()
显式地调用它或是引发了AttributeError
异常。此方法应当返回找到的属性值或是引发一个AttributeError
异常。
注意:
__getattr__
示例中,我们通过直接访问self.__dict__
来规避触发__getattr__
方法,但是__getattribute__
在访问对象的已有属性时都会触发,即使是self.__dict__
也会引发无限循环,所以__getattribute__
方法中不能使用self.__dict__
,解决办法是调用父类的__getattribute__
方法,即在__getattribute__
中通过super().__getattribute__(item)
来读取对象的属性。
__getattribute__
方法看着很强大,但是由于其敏感性,和容易引起无限循环,所以,正常情况下,并不建议覆写此方法。只有当自己明明白白知道需要通过此方法完成什么效果时才使用。
# 示例代码-只定义了__getattr__方法,没有定义__getattribute__方法时
class Cat:
def __init__(self, name):
print('__init__被调用了')
self.name = name
def __getattr__(self, item):
print('__getattr__被调用了')
return 18
if __name__ == '__main__':
tom = Cat('Tom')
print(tom.name)
print(tom.age)
# 示例代码-只定义了__getattr__方法,没有定义__getattribute__方法时
class Cat:
def __init__(self, name):
print('__init__被调用了')
self.name = name
def __getattr__(self, item):
print('__getattr__被调用了')
return 18
if __name__ == '__main__':
tom = Cat('Tom')
print(tom.name)
print(tom.age)
# 输出结果
__init__被调用了
Tom
__getattr__被调用了
18
# 输出结果
__init__被调用了
Tom
__getattr__被调用了
18
当只定义了__getattr__
方法,没有定义__getattribute__
方法时,调用不存在的属性或者方法,会触发AttributeError
,然后由__getattr__
方法进行处理
# 示例代码-当同时定义了__getattr__和__getattribute__方法时
class Cat:
def __init__(self, name):
print('__init__被调用了')
self.name = name
def __getattr__(self, item):
print('__getattr__被调用了')
return 18
def __getattribute__(self, item):
print('__getattribute__被调用了')
if item == 'gender':
raise AttributeError
return 22
if __name__ == '__main__':
tom = Cat('Tom')
print(tom.name)
print(tom.age)
print(tom.gender)
# 示例代码-当同时定义了__getattr__和__getattribute__方法时
class Cat:
def __init__(self, name):
print('__init__被调用了')
self.name = name
def __getattr__(self, item):
print('__getattr__被调用了')
return 18
def __getattribute__(self, item):
print('__getattribute__被调用了')
if item == 'gender':
raise AttributeError
return 22
if __name__ == '__main__':
tom = Cat('Tom')
print(tom.name)
print(tom.age)
print(tom.gender)
# 输出结果
__init__被调用了
__getattribute__被调用了
22
__getattribute__被调用了
22
__getattribute__被调用了
__getattr__被调用了
18
# 输出结果
__init__被调用了
__getattribute__被调用了
22
__getattribute__被调用了
22
__getattribute__被调用了
__getattr__被调用了
18
当同时定义了__getattr__
和__getattribute__
方法时,调用不存在的属性或者方法,优先调用__getattribute__
方法,只有当__getattribute__
方法抛出AttributeError
异常时,才会调用__getattr__
方法
对象的属性查找顺序如下:实例的
__getattribute__
→ 实例对象字典 → 实例所在类字典 → 实例所在类的父类(MRO
顺序)字典 → 实例所在类的__getattr__
→ 报错
__setattr__
此方法在一个属性被尝试赋值时被调用。这个调用会取代正常机制,即将值保存到实例字典。__delattr__
类似于__setattr__()
,但其作用为删除而非赋值。此方法应该仅在del obj.name
对于该对象有意义时才被实现。__dir__
此方法会在对相应对象调用dir()
时被调用,返回值必须为一个序列。dir()
会把返回的序列转换为列表并对其排序。
9.属性描述符
__get__
调用此方法以获取所有者类的属性(类属性访问)或该类的实例的属性(实例属性访问)。可选的owner
参数是所有者类而instance
是被用来访问属性的实例,如果通过owner
来访问属性则返回None
。
此方法应当返回计算得到的属性值或是引发AttributeError
异常。__set__
调用此方法以设置instance
指定的所有者类的实例的属性为新值value
。__delete__
调用此方法以删除instance
指定的所有者类的实例的属性。
一个类只要实现了
__get__
,__set__
,__delete__
中任意一个方法,我们就可以叫它描述器(descriptor
)。如果只定义了__get__
我们叫非资料描述器(non-data descriptor
),如果__set__
,__delete__
任意一个/或者同时出现,我们叫资料描述器(data descriptor
)。
# 示例代码
class NonDataDescriptor:
def __get__(self, instance, owner):
print(instance, owner)
return '__get__方法被调用了'
class DataDescriptor:
def __get__(self, instance, owner):
print(instance, owner)
return '__get__方法被调用了'
def __set__(self, instance, value):
print(instance, value)
return '__set__方法被调用了'
class Test:
non_data_des = NonDataDescriptor()
data_des = DataDescriptor()
if __name__ == '__main__':
t = Test()
print("测试非资料描述器"+"-"*50)
print(t.non_data_des)
print(Test.non_data_des)
print("通过实例对描述器进行赋值"+"-"*50)
t.non_data_des = 1
print(t.non_data_des)
print(Test.non_data_des)
print("通过类对描述器进行赋值"+"-"*50)
Test.non_data_des = 1
print(t.non_data_des)
print(Test.non_data_des)
print("测试非资料描述器完成"+"-"*50)
print("测试资料描述器"+"-"*50)
print(t.data_des)
print(Test.data_des)
print("通过实例对描述器进行赋值"+"-"*50)
t.data_des = 1
print(t.data_des)
print(Test.data_des)
print("通过类对描述器进行赋值"+"-"*50)
Test.data_des = 1
print(t.data_des)
print(Test.data_des)
print("测试资料描述器完成"+"-"*50)
# 示例代码
class NonDataDescriptor:
def __get__(self, instance, owner):
print(instance, owner)
return '__get__方法被调用了'
class DataDescriptor:
def __get__(self, instance, owner):
print(instance, owner)
return '__get__方法被调用了'
def __set__(self, instance, value):
print(instance, value)
return '__set__方法被调用了'
class Test:
non_data_des = NonDataDescriptor()
data_des = DataDescriptor()
if __name__ == '__main__':
t = Test()
print("测试非资料描述器"+"-"*50)
print(t.non_data_des)
print(Test.non_data_des)
print("通过实例对描述器进行赋值"+"-"*50)
t.non_data_des = 1
print(t.non_data_des)
print(Test.non_data_des)
print("通过类对描述器进行赋值"+"-"*50)
Test.non_data_des = 1
print(t.non_data_des)
print(Test.non_data_des)
print("测试非资料描述器完成"+"-"*50)
print("测试资料描述器"+"-"*50)
print(t.data_des)
print(Test.data_des)
print("通过实例对描述器进行赋值"+"-"*50)
t.data_des = 1
print(t.data_des)
print(Test.data_des)
print("通过类对描述器进行赋值"+"-"*50)
Test.data_des = 1
print(t.data_des)
print(Test.data_des)
print("测试资料描述器完成"+"-"*50)
# 测试结果
测试非资料描述器--------------------------------------------------
<__main__.test>0x00000198E0DE2610> <class '__main__.Test'>__get__方法被调用了None <class '__main__.Test'>__get__方法被调用了
通过实例对描述器进行赋值--------------------------------------------------
1None <class '__main__.Test'>__get__方法被调用了
通过类对描述器进行赋值--------------------------------------------------
1
1
测试非资料描述器完成--------------------------------------------------
测试资料描述器--------------------------------------------------
<__main__.Test object at 0x00000198E0DE2610> <class '__main__.Test'>__get__方法被调用了None <class '__main__.Test'>__get__方法被调用了
通过实例对描述器进行赋值--------------------------------------------------
<__main__.Test object at 0x00000198E0DE2610> 1
<__main__.Test object at 0x00000198E0DE2610> <class '__main__.Test'>__get__方法被调用了None <class '__main__.Test'>__get__方法被调用了
通过类对描述器进行赋值--------------------------------------------------
1
1
测试资料描述器完成--------------------------------------------------
# 测试结果
测试非资料描述器--------------------------------------------------
<__main__.test>0x00000198E0DE2610> <class '__main__.Test'>__get__方法被调用了None <class '__main__.Test'>__get__方法被调用了
通过实例对描述器进行赋值--------------------------------------------------
1None <class '__main__.Test'>__get__方法被调用了
通过类对描述器进行赋值--------------------------------------------------
1
1
测试非资料描述器完成--------------------------------------------------
测试资料描述器--------------------------------------------------
<__main__.Test object at 0x00000198E0DE2610> <class '__main__.Test'>__get__方法被调用了None <class '__main__.Test'>__get__方法被调用了
通过实例对描述器进行赋值--------------------------------------------------
<__main__.Test object at 0x00000198E0DE2610> 1
<__main__.Test object at 0x00000198E0DE2610> <class '__main__.Test'>__get__方法被调用了None <class '__main__.Test'>__get__方法被调用了
通过类对描述器进行赋值--------------------------------------------------
1
1
测试资料描述器完成--------------------------------------------------
描述器的调用是因为__getattribute__
,重写 __getattribute__
方法会阻止正常的描述器调用,资料描述器总是比实例字典优先,而非资料描述器可能被实例字典重写(非资料描述器不如实例字典优先)。
10.跟类相关的服务
__prepare__
命名空间钩子
如果元类具有__prepare__
属性,它会以namespace = metaclass.__prepare__(name, bases, **kwds)
的形式被调用(其中如果有任何额外的关键字参数,则应当来自类定义)。__prepare__
方法应该被实现为classmethod()
。__prepare__
所返回的命名空间会被传入__new__
,但是当最终的类对象被创建时,该命名空间会被拷贝到一个新的dict
中。
如果元类没有__prepare__
属性,则类命名空间将初始化为一个空的有序映射。__instancecheck__
如果 instance 应被视为 class 的一个(直接或间接)实例则返回真值。如果定义了此方法,则会被调用以实现isinstance(instance, class)
。__subclasscheck__
Return true 如果 subclass 应被视为 class 的一个(直接或间接)子类则返回真值。如果定义了此方法,则会被调用以实现issubclass(subclass, class)
。
通过__instancecheck__()
和__subclasscheck__()
来定制isinstance()
和issubclass()
行为,加入此功能的动机是出于向该语言添加抽象基类的内容
11.一元运算符
__neg__
-__pos__
+__abs__
abs()__invert__
~
调用此方法以实现一元算术运算 (-
,+
,abs()
和~
)。
12.众多比较运算符
__lt__
<__le__
<=__eq__
==__ne__
!=__gt__
>__ge__
>=
以上这些被称为“富比较”方法。运算符号与方法名称的对应关系如下:x 调用 x.__lt__(y)、x<=y 调用 x.__le__(y)、x==y 调用 x.__eq__(y)、x!=y 调用 x.__ne__(y)、x>y 调用 x.__gt__(y)、x>=y 调用 x.__ge__(y)。
如果指定的参数对没有相应的实现,富比较方法可能会返回单例对象NotImplemented
。按照惯例,成功的比较会返回False
或True
。不过实际上这些方法可以返回任意值,因此如果比较运算符是要用于布尔值判断(例如作为if
语句的条件),Python 会对返回值调用bool()
以确定结果为真还是假。
在默认情况下,object
通过使用is
来实现__eq__()
,并在比较结果为假值时返回NotImplemented
:True if x is y else NotImplemented
。对于__ne__()
,默认会委托给__eq__()
并对结果取反,除非结果为NotImplemented
。比较运算符之间没有其他隐含关系或默认实现;例如,(x 为真并不意味着 x<=y。
13.算术运算符
__add__
+__sub__
-__mul__
*__truediv__
/__floordiv__
//__mod__
%__divmod__
divmod()__pow__
** 或 pow()__round__
round()
调用这些方法来实现二进制算术运算 (+
,-
,*
,@
,/
,//
,%
,divmod()
,pow()
,**
,<<
,>>
,&
,^
,|
)。例如,求表达式x + y
的值,其中 x 是具有__add__()
方法的类的一个实例,则会调用x.__add__(y)
。__divmod__()
方法应该等价于使用__floordiv__()
和__mod__()
,它不应该被关联到__truediv__()
。请注意如果要支持三元版本的内置pow()
函数,则__pow__()
的定义应该接受可选的第三个参数。
如果这些方法中的某一个不支持与所提供参数进行运算,它应该返回NotImplemented
。
14.反向算术运算符
__radd__
+__rsub__
-__rmul__
*__rtruediv__
/__rfloordiv__
//__rmod__
%__rdivmod__
divmod()__rpow__
pow()
调用这些方法来实现具有反射(交换)操作数的二进制算术运算 (+
,-
,*
,@
,/
,//
,%
,divmod()
,pow()
,**
,<<
,>>
,&
,^
,|
)。这些成员函数仅会在左操作数不支持相应运算且两个操作数类型不同时被调用。例如,求表达式x - y
的值,其中 y 是具有__rsub__()
方法的类的一个实例,则当x.__sub__(y)
返回NotImplemented
时会调用y.__rsub__(x)
。
15.增量赋值算术运算符
__iadd__
+=__isub__
-=__imul__
*=__itruediv__
/=__ifloordiv__
//=__imod__
%=__ipow__
**=
调用这些方法来实现扩展算术赋值 (+=
,-=
,*=
,@=
,/=
,//=
,%=
,**=
,<<=
,>>=
,&=
,^=
,|=
)。这些方法应该尝试进行自身操作 (修改 self) 并返回结果 (结果应该但并非必须为 self)。如果某个方法未被定义,相应的扩展算术赋值将回退到普通方法。例如,如果 x 是具有__iadd__()
方法的类的一个实例,则x += y
就等价于x = x.__iadd__(y)
。否则就如x + y
的求值一样选择x.__add__(y)
和y.__radd__(x)
。在某些情况下,扩展赋值可导致未预期的错误 ,但此行为实际上是数据模型的一个组成部分。
16.位运算符
-
__invert__
~ -
__lshift__
<< -
__rshift__
>> -
__and__
& -
__or__
| -
__xor__
^
调用此方法以实现位运算 (~
, <<
, >>
, &
,|
,^
)。
17.反向位运算符
-
__rlshift__
<< -
__rrshift__
>> -
__rand__
& -
__rxor__
^ -
__ror__
|
调用此方法以实现反向位运算 ( <<
, >>
, &
,^
,|
)。
18.增量赋值位运算符
-
__ilshift__
<<= -
__irshift__
>>= -
__iand__
&= -
__ixor__
^= -
__ior__
|=
调用此方法以实现反向位运算 (<<=
,>>=
,&=
,^=
,|=
)。