魔法属性涉及了类和对象的一些基本信息。
class Father(object):
pass
class Magic(Father):
'''doc 的内容'''
a = 1
def __init__(self):
self.b = 2
# 下面的魔法属性,如果没有额外说明,则通过类名访问和通过实例访问结果相同。
# 应当注意的是,python中的类本身也是type类的对象。
# 类名
print(Magic.__class__) # <class 'type'> # 这里是class的class
print(Magic().__class__) # <class '__main__.Magic'>
# 模块名
print(Magic.__module__ ) # __main__
print(Magic.__name__) # Magic # 只能用类名访问
print(__name__) # __main__ # 常用于判断是否是导入的文件
# 实例属性列表,不会打印类的属性
print(Magic.__dict__) # {'a': 1, '__init__': ...} (后面省略)
print(Magic().__dict__) # {'b': 2}
# 父类的继承顺序,当访问一个属性时,在继承链条上的查询顺序,只能用类名访问。
# 这个顺序的算法比较复杂,见 https://www.bilibili.com/video/BV1V5411S7dY
print(Magic.__mro__) # (<class '__main__.Magic'>, <class '__main__.Father'>, <class 'object'>)
# 说明文档
print(Magic.__doc__) # doc 的内容
# 列出所有子类,只能通过类名访问。
# 访问object的子类会打印一大堆东西。
print(Father.__subclasses__()) # [<class '__main__.Magic'>]
魔术方法的官方称谓是 special method,又因为其名字被双下划线包裹的特点,被称为 dunder(double underscore) method,用于对类进行客制化操作。
主要是__new__()
, __init__()
, __del__()
三个方法,其中__init__()
方法最常用。
__new__
方法是静态的,因为它是构造函数,调用该函数时,类对象尚未创建,所以其传入一个cls作为参数。返回构造完成的对象。__init__
方法用于初始化,需要传入对象本身self作为参数。在__new__
方法调用成功之后调用。__del__
方法当对象被销毁(对象的引用是0,或被垃圾回收)时调用。关于__new__方法,进一步的讨论见 Python MetaClass元类详解
class Magic(object):
def __new__(cls, *args, **kwargs):
print("__new__:", cls, args, kwargs) # cls对象打印的是实例.__class__的结果
ret = super().__new__(cls) # 调用父类的__new__()方法创建对象
# 在此处进行修改可以控制返回的对象
return ret
def __init__(self, attr):
super().__init__() # 调用父类的构造函数来对父类成员进行初始化
print("__init__:", self, attr)
def __del__(self):
print("__del__")
m = Magic("magic");
'''
# 对象在被创建时,参数会同时传入__new__和__init__方法中
__new__: <class '__main__.Magic'> ('magic',) {}
__init__: <__main__.Magic object at 0x0000022BB03EFBB0> magic
__del__
'''
使用__new__
方法实现单例模式(Singleton)
class Singleton(object):
def __new__(cls, *args, **kwargs):
if not hasattr(cls, '_instance'):
cls._instance = super().__new__(cls, *args, **kwargs)
return cls._instance
a = Singleton()
b = Singleton()
a == b # True
__init_subclass__(cls, ...)
会在子类被创建时调用。
class Base:
def __init_subclass__(cls, name):
cls.x = 1
cls.name = name
class A(Base, name="sub"):
pass
print(A.x) #1
print(A.name) #sub
__call__()
在对象中定义了__call__()
方法之后,该对象就成为一个 callable,等价于一个函数。事实上,一切可调用的对象,都实现了__call__()
方法。
class Magic():
def __call__(self, *args, **kwargs):
print(self, args, kwargs)
m = Magic();
m("arg", args1="abc") # 等价于 m.__call__("arg", args1="abc")
# <__main__.Magic object at 0x00000142A7537C40> ('arg',) {'args1': 'abc'}
__hash__()
返回一个整数。用于通过hash(obj)方法计算对象的哈希值,在集合和字典数据结构中用于作为 key 的计算值。
推荐方法:在重载过程中调用 Python 内建的hash()
函数
python中,集合在添加新元素时,会先计算hash值,如果没有冲突,则直接添加,如果出现冲突,则判断两个对象是否相等(
__eq__
),如果相等,则丢弃新元素。
__eq__
和__hash__
方法在object类中都有默认的,基于对象内存地址的实现。如果只重写__eq__
方法,那么__hash__
方法会被清除,对象会变成 unhashable,此时对象也无法添加入集合或字典。所以,重写__eq__
方法后,一般也要重写__hash__
方法。
class Magic(object):
def __init__(self, a):
self.a = a
def __hash__(self):
return 100
def __eq__(self, other):
return True
s = set()
s.add(Magic(1))
s.add(Magic(2))
for i in s:
print(i.a) # 1
主要是__getattr__
, __getattribute__
, __setattr__
, __delattr__
四个方法。当使用点方法进行属性访问时调用。
class Magic():
# 访问不存在的类属性时调用
def __getattr__(self, name):
print("Magic has no", name)
return None # 如果没有返回值,默认返回None
# 属性拦截器,使用点方法访问任意属性时调用。注意:此处对拦截一切属性都生效,包括各种内建属性、类方法等
def __getattribute__(self, name):
print("seek Magic for", name)
# 此处不能写return self.name,会无限递归
return super().__getattribute__(self, name)
# return object.__getattribute__(self, name)
def __setattr__(self, name, value):
print("Magic." + name, " = ", value)
# 此处不能写 self.name = value,会无限递归
object.__setattr__(self, name, value)
# 使用 del 关键词删除属性时调用
def __delattr__(self, name):
print("del Magic." + name)
super().__delattr__(name)
m = Magic();
m.a = 123
print(m.a)
print(m.b)
'''
Magic.a = 123
seek Magic for a
123
seek Magic for b
Magic has no b
None
'''
此外还有与描述器有关的
__get__()
、__set__()
、__delete__()
方法,此处不再展开。相关内容请参考:Python 黑魔法:描述器(descriptor)。
__str__(self) -> string # str(实例)或print(实例)等需要转字符串时调用,返回一个字符串。__repr__与__str__都被定义时会优先调用此方法。
__repr__(self) -> string # 与__str__方法作用相似,是__str__未定义时的默认输出,或通过repr(实例)调用。打印容器时会优先调用此方法。
__bool__(self) -> bool # 返回一个布尔值,当处于布尔表达式中时调用。
__format__(self) -> string # 在format(实例)或f-string时调用。
__bytes__(self) -> bytes # bytes(实例)时调用。
__dir__(self) -> list # dir(实例)时调用,返回所有属性列表,一般不需要重写。注意与__dict__区别,__dict__仅包含实例属性。
重载比较运算符,建议返回一个布尔值。
__lt__(self, other) self < other
__le__(self, other) self <= other
__eq__(self, other) self == other
__ne__(self, other) self != other
__gt__(self, other) self > other
__ge__(self, other) self >= other
__eq__
如果未被定义,其默认实现是is
操作。
如果定义了__eq__
却没定义__ne__
,计算!=
时会调用__eq__
然后取反。相应的,小于和大于,小于等于和大于等于也是一对。
重载比较运算符,返回一个自定义结果。
__add__(self, other) self + other
__sub__(self, other) self - other
__mul__(self, other) self * other
__matmul__(self, other) self @ other
__truediv__(self, other) self / other
__floordiv__(self, other) self // other
__mod__(self, other) self % other
__divmod__(self, other) divmod(self, other)
__pow__(self, other[, modulo]) power(self, other[, mod]) 或 self ** other [% mod]
__lshift__(self, other) self << other
__rshift__(self, other) self >> other
__and__(self, other) self & other
__xor__(self, other) self ^ other
__or__(self, other) self | other
# 上述方法有对应的r版本。当运算符左边的运算数未定义相关运算时,会调用右边对象的r版本运算。
# 此时会将右边的对象传入self参数。实现方法与原版基本一致。
__rmul__(self, other) other * self
此处可以用isinstance(other, type)
来对 other 进行讨论,实现对不同数据类型的运算。
双目运算符与赋值操作的结合。直接在左边操作数上面进行修改,一般是返回self。
__iadd__(self, other) self += other
__isub__(self, other) self -= other
__imul__(self, other) self *= other
__itruediv__(self, other) self /= other
__ifloordiv__(self, other) self //= other
__imod__(self, other) self %= other
__ipow__(self, other[, modulo]) self **= other
__ilshift__(self, other) self <<= other
__irshift__(self, other) self >>= other
__iand__(self, other) self &= other
__ixor__(self, other) self ^= other
__ior__(self, other) self |= other
__pos__(self) +self
__neg__(self) -self
__abs__(self) abs(self)
__invert__(self) ~self
需要被显式转换为其他类型时才会调用,隐式转换不调用。返回值必须是函数名对应的类型。
__str__(self) str(self)
__complex__(self) complex(self)
__int__(self) int(self)
__float__(self) float(self)
__round__(self[, n]) round(self)
__trunc__(self) trunc(self) # math库中的函数
__floor__(self) floor(self) # math库中的函数
__ceil__(self) ceil(self) # math库中的函数
class Magic():
def __init__(self):
self.a = "before the with"
def __enter__(self):
self.a = "in the with"
return self # 此处返回值会绑定到as后面的变量上
def __exit__(self, exctype, excvalue, traceback):
self.a = "after the with"
with Magic() as m:
print(m.a) # in the with
print(m.a) # after the with
用于构造容器,如自定义的列表或者字典等。
__len__(self) len(self) # 如果没有定义__bool__方法,当对象进行逻辑运算时会调用__len__
__getitem__(self, key) self[key]
__setitem__(self, key, value) self[key] = value
__delitem__(self, key) del self[key]
__reversed__(self) reversed(self)
__contains__(self, item) item in self
用于构造一个迭代器,使得该类的对象可以放在for循环中遍历。主要用到__iter__
和__next__
两个方法。
# 一个数字1到10的迭代器
class Magic(object):
_max = 10
def __iter__(self):
# 此处进行迭代器的初始化
self.a = 0
return self # 必须return self,否则就构建不了迭代器
def __next__(self):
# 此处决定每次迭代生成的对象
if self.a < self._max:
self.a += 1
else:
# 设置终止迭代的条件
raise StopIteration
return self.a
for i in Magic():
print(i, end=' ') # 1 2 3 4 5 6 7 8 9 10
评论区