什么是魔法方法?
魔法方法(magic methods):python中的魔法方法是指方法名以两个下划线开头并以两个下划线结尾的方法,因此也叫Dunder Methods (Double Underscores)。常用于运算符重载。魔法方法会在对类的某个操作时后端自动调用,而不需要自己直接调用。例如当使用+将两个数字相加时,在类内部会调用__add__()方法,再比如创建一个类A的对象,a=A(),python就会自动调用__new__和__init__。
常见魔法方法
初始化和构造
方法名 | 描述 |
---|
_new_(cls, other) | 在实例化一个对象的时候被调用 | _init_(self, other) | 用于初始化对象,被__new__方法调用 | _del_(self) | 对象的析构方法 |
有几个问题需要注意一下:
- 只有继承object的新式类才有_new_
- __new__至少有一个参数cls,代表当前类
- __new__必须要有返回值,返回创建的对象
- __init__必须有一个参数self,这个是__new__的返回值, __init__不需要返回值
测试代码:
class MyClass(object):
def __init__(self, num):
self.num = num
print("i am __init__ function")
def __new__(cls, num):
print("i am __new__ function")
obj = super(MyClass, cls).__new__(cls)
return obj
def __del__(self):
print("i am __del__ function")
myclass = MyClass(num=55)
print("create obj ok === ")
del myclass
输出结果:
i am __new__ function
i am __init__ function
create obj ok ===
i am __del__ function
一元操作符
方法名 | 描述 |
---|
_pos_(self) | 会被取正操作符调用,例如 +a | _neg_(self) | 会被取反操作符调用,例如 -a | _abs_(self) | 在调用内置函数abs()的时候被调用, 取绝对值 | _invert_(self) | 在使用~操作符的时候被调用, 按位取反 | _round_(self, n) | 执行内置函数round()的时候被调用, 四舍五入取近似值,n为小数位数 | _floor_(self) | 执行内置函数math.floor()的时候被调用, 取小于等于该值的最大整数 | _ceil_(self) | 执行内置函数math.ceil()时被调用,取大于等于该值的最小整数 | _trunc_(self) | 执行math.trunc()函数时被调用, 取该值的截断整数 |
测试代码:
class TestUnaryOpsAndFunc:
def __init__(self, num=0) -> None:
self._num = 3
self._neg_num = -5
self._float_num = 3.1415
self._neg_float_num = -3.1415
def __pos__(self):
return +self._neg_num
def __neg__(self):
return -self._num
def __abs__(self):
return abs(self._neg_float_num)
def __invert__(self):
return ~self._num
def __round__(self):
return round(self._float_num)
def __floor__(self):
return math.floor(self._neg_float_num)
def __ceil__(self):
return math.ceil(self._float_num)
def __trunc__(self):
return math.trunc(self._float_num)
test_class = TestUnaryOpsAndFunc()
print("pos: ", +test_class)
print("neg: ", -test_class)
print("abs: ", abs(test_class))
print("invert: ", ~test_class)
print("round: ", round(test_class))
print("floor: ", math.floor(test_class))
print("ceil: ", math.ceil(test_class))
print("trunc: ", math.trunc(test_class))
测试结果:
pos: -5
neg: -3
abs: 3.1415
invert: -4
round: 3
floor: -4
ceil: 4
trunc: 3
增量赋值
方法名 | 描述 |
---|
_iadd_(self, other) | 在执行加法增量赋值的时候调用,例如: a += b | _isub_(self, other) | 在执行减法增量赋值的时候调用,例如: a -= b | _imul_(self, other) | 在执行乘法法增量赋值的时候调用,例如: a *= b | _ifloordiv_(self, other) | 在执行整数除法增量赋值的时候调用,例如: a //= b,结果向下取整 | _idiv_(self, other) | 在执行除法增量赋值的时候调用,例如: a /= b | _itruediv_(self, other) | 在执行真实除法增量赋值的时候调用 | _imod_(self, other) | 在执行取余增量赋值的时候调用,例如: a %= b | _ipow_(self, other) | 在执行幂增量赋值的时候调用,例如: a **= b | _ilshift_(self, other) | 在执行向左移位增量赋值的时候调用,例如: a <<= b | _irshift_(self, other) | 在执行向右移位增量赋值的时候调用,例如: a >>= b | _iand_(self, other) | 在执行按位与增量赋值的时候调用,例如: a &= b | _ior_(self, other) | 在执行按位或增量赋值的时候调用,例如: a | _ixor_(self, other) | 在执行按位异或增量赋值的时候调用,例如: a ^= b |
类型转换
方法名 | 描述 |
---|
_init_(self) | 被内置方法int()调用,来将一个类型转换为int | _long_(self) | 被内置方法long()调用,来将一个类型转换为long | _float_(self) | 被内置方法float()调用,来将一个类型转换为float | _complex_(self) | 被内置方法complex()调用,来将一个类型转换为complex | _oct_(self) | 被内置方法oct()调用,来将一个类型转换为octal, 也就是将一个对象转换为八进制 | _hex_(self) | 被内置方法hex()调用,来将一个类型转换为hexadecimal, 也就是将一个对象转换为十六进制 | _index_(self) | 当对象被用来表示切片的时候, 会调用该函数来转换为int类型 | _trunc_(self) | 执行math.trunc()方法时被调用, 取该值的截断整数 |
字符串相关
方法名 | 描述 |
---|
_str_(self) | 被内置方法str()调用, 返回一个字符串来描述该类型 | _repr_(self) | 被内置方法repr()的参数时被调用, 返回一个机器可读的表达,这里机器可读意思是返回值可以在命令行上执行 | _unicode_(self) | 被内置方法unicode()调用, 返回一个unicode的字符串 | _format_(self,formatstr) | 被内置方法string.fromat()调用, 返回一个新格式的字符串, 例如: “name: {0}”.format(a), 会调用 xxxxxxxxx | _hash_(self) | 被内置方法hash()调用, 返回一个整型数 | _nonzero_(self) | 被内置方法bool()调用, 返回True或False, 在python3中重命名成_bool_ | _dir_(self) | 被内置方法dir()调用, 返回类的属性列表 | _sizeof_(self) | 被内置方法sys.getsizeof()调用,返回一个对象的大小 |
str和repr的区别后面单独写个文章来介绍一下
测试代码:
class Person:
def __init__(self, name, age) -> None:
self._name = name
self._age = age
def __str__(self) -> str:
output = "str called,name: {0}; age: {1}".format(self._name, self._age)
return output
def __repr__(self) -> str:
output = "repr called, name: {0}; age: {1}".format(self._name, self._age)
return output
person = Person(name="william", age=24)
print(person)
print(repr(person))
测试结果:
str called,name: william; age: 24
repr called, name: william; age: 24
属性相关
方法名 | 描述 |
---|
_getattr_(self, name) | 当访问类中不存在的属性时被调用 | _setattr_(self, name, value) | 当给一个类的属性赋值时被调用 | _getattribute_(self, name) | 当访问类中不存在的属性时被调用,优先调用 | _setattribute_(self, name) | 当访问类中不存在的属性时被调用,优先调用 | _delattr_(self, name) | 当删除一个类的属性时被调用 | _dir_(self) | 被内置方法dir()调用, 返回类的属性列表 |
描述符
要创建一个描述符, 该类必须实现_get_, _set_, __delete__三个方法中至少一个
方法名 | 描述 |
---|
_get_(self, instance, owner) | 获取属性值时被调用 | _set_(self, instance, value) | 设置属性值时被调用 | _delete_(self, instance, owner) | 删除属性值时被调用 |
测试代码:
class Attribute:
def __init__(self, data) -> None:
self._data = data
def __get__(self, instance, owner):
print("__get__ called")
return self._data
def __set__(self, instance, value):
print("__set__ called")
self._data = value
def __delete__(self, instance):
print("__delete__ called")
del self._data
class MyClass:
attr = Attribute(99)
def __init__(self, attr="defaule value"):
self.attr = attr
myclass = MyClass()
myclass.attr = "new value"
value = myclass.attr
print("value: ", value)
del myclass.attr
myclass1 = MyClass()
value = myclass1.attr
print("value: ", value)
测试结果:
__set__ called
__set__ called
__get__ called
value: new value
__delete__ called
__set__ called
__get__ called
value: defaule value
拷贝
方法名 | 描述 |
---|
_copy_(self) | 被copy.copy()调用,返回一个对象的浅拷贝, 一个新实例包含的数据是引用 | _deepcopy_(self, memodict) | 被copy.deepcopy()调用, 返回一个对象的深拷贝, 对象数据全部拷贝一份 |
协程
方法名 | 描述 |
---|
_await_(self) | 实现__await__方法的类,构造出的对象就是awaitbale的, 返回一个迭代器供await方法使用 | _aiter_(self) | 异步迭代器async for中使用,返回一个异步iterator对象, 也是self | _anext_(self) | 一个异步iterator必须实现__anext__方法, 返回awaitbale | _aenter_(self) | 异步上下文管理器 async with,进入时调用 | _aexit_(self) | 异步上下文管理器 async with, 退出时调用 |
测试代码
class Awaitable:
def __init__(self) -> None:
self._person = []
p = Person(name="william", age=24)
p1 = Person(name="william1", age=25)
p2 = Person(name="william2", age=26)
p3 = Person(name="william3", age=27)
self._person.append(p)
self._person.append(p1)
self._person.append(p2)
self._person.append(p3)
def __aiter__(self):
print("aiter called ...")
self.index = 0
return self
async def __anext__(self):
print("anext called")
while self.index < len(self._person):
self.index += 1
return self._person[self.index - 1]
raise StopAsyncIteration
async def AsyncFunction():
print("called async function")
return 1
async def test_coroutine():
func = AsyncFunction()
await func
async def test_async_context():
async with AsyncContext() as ac:
print("main block")
async def test_async_for():
async for a in Awaitable():
print("a: ", a)
asyncio.run(test_coroutine())
asyncio.run(test_async_for())
asyncio.run(test_async_context())
测试结果:
called async function
aiter called ...
anext called
a: str called,name: william; age: 24
anext called
a: str called,name: william1; age: 25
anext called
a: str called,name: william2; age: 26
anext called
a: str called,name: william3; age: 27
anext called
enter the block
main block
exit the block
二元运算符
方法名 | 描述 |
---|
_add_(self, other) | 当使用+执行加法运算的时候被调用 | _sub_(self, other) | 当使用-执行减法运算的时候被调用 | _mul_(self, other) | 当使用*执行乘法运算的时候被调用 | _div_(self, other) | 当使用/执行除法运算的时候被调用 | _floordiv_(self, other) | 当使用//执行取整除法运算的时候被调用 | _truediv_(self, other) | 当使用/执行除法运算的时候被调用,只有from _future_ import division时才起作用 | _mod_(self, other) | 当使用%执行取余运算的时候被调用 | _pow_(self, other[,modulo]) | 当使用**执行幂运算的时候被调用 | _lshift_(self, other) | 当使用<<执行左移运算的时候被调用 | _rshift_(self, other) | 当使用>>执行右移运算的时候被调用 | _and_(self, other) | 当使用&执行按位与运算的时候被调用 | _xor_(self, other) | 当使用^执行按位异或运算的时候被调用 | _or_(self, other) | 当使用|执行按位或运算的时候被调用 | _lt_(self, other) | 当使用<运算符进行比较时被调用 | _le_(self, other) | 当使用<=运算符进行比较时被调用 | _eq_(self, other) | 当使用==运算符进行比较时被调用 | _ne_(self, other) | 当使用!=运算符进行比较时被调用 | _ge_(self, other) | 当使用>=运算符进行比较时被调用 |
反射算数运算符
反射运算是什么呢? 比如: a + b, 反射的意思就是交换两个操作数的位置: b + a, 注意这时候b一定不能够定义非反射的操作, 只有这样才会调用a的反射操作_radd()_
方法名 | 描述 |
---|
_radd_(self, other) | 当使用+执行反射加法运算的时候被调用 | _rsub_(self, other) | 当使用-执行反射减法运算的时候被调用 | _rmul_(self, other) | 当使用*执行反射乘法运算的时候被调用 | _rfloordiv_(self, other) | 当使用//执行反射取整除法运算的时候被调用 | _rtruediv_(self, other) | 当使用/执行反射除法运算的时候被调用 | _rmod_(self, other) | 当使用%执行反射取余运算的时候被调用 | _rpow_(self, other[,modulo]) | 当使用**执行反射幂运算的时候被调用 | _rlshift_(self, other) | 当使用<<执行反射左移运算的时候被调用 | _rrshift_(self, other) | 当使用>>执行反射右移运算的时候被调用, 需要再确认一下 | _rand_(self, other) | 当使用&执行按位与运算的时候被调用 | _rxor_(self, other) | 当使用^执行按位异或运算的时候被调用 | _ror_(self, other) | 当使用|执行按位或运算的时候被调用 |
集合相关
方法名 | 描述 |
---|
_len_(self) | 被内置方法len()调用, 返回容器的长度, 可变和不可变的容器类型都需要实现 | _getitem_(self, key) | 在使用key访问成员时调用, object[key] | _setitem_(self, key, value) | 给集合中key值赋值时被调用, object[key] = value | _delitem_(self, key) | 被del 调用, 例如: del object[key] | _contains_(self) | 在执行in()或者not in时被调用 | _reversed_(self) | 被内置方法reversed()调用, 翻转序列的顺序, | _missing_(self) | 在字典的子类中使用,当试图访问字典中不存在的key时被调用 |
迭代相关
方法名 | 描述 |
---|
_iter_(self) | 被内置方法iter()调用或者for item in container时调用, 要在__iter__中return self | _next_(self) | |
测试代码:
class MyQueue:
def __init__(self) -> None:
self._queue = [i for i in range(10)]
def __iter__(self):
self._index = 0
return self
def __next__(self):
while self._index < len(self._queue) - 1:
self._index += 1
return self._queue[self._index]
raise StopIteration
it = iter(MyQueue())
while True:
try:
num = next(it)
print("num: ", num)
except StopIteration:
print("visit finished")
break
num: 1
num: 2
num: 3
num: 4
num: 5
num: 6
num: 7
num: 8
num: 9
visit finished
调用
方法名 | 描述 |
---|
_call_(self) | 使类对象可以像函数一样调用, object() |
测试代码:
class Dog:
def __init__(self, color) -> None:
self._color = color
def __call__(self, *args: Any, **kwds: Any) -> Any:
return self._color
dog = Dog(color="black")
color = dog()
print("color: ", color)
测试结果:
color: black
上下文管理器
方法名 | 描述 |
---|
_enter_(self) | 最开始上下文管理器要做的操作, __enter__的返回值会赋给as后面的对象 | _exit_(self, exception_type, exception_value, traceback) | 在退出该语句块时要执行的操作 |
测试代码:
class MyContex:
def __enter__(self):
print ("enter the closer")
def __exit__(self, exception_type, exception_value, traceback):
print("exit the closer")
with MyContex() as context:
print("it is in closer")
测试结果:
enter the closer
it is in closer
exit the closer
|