在上篇文章《Python笔记 · 私有方法、私有属性 & 单下划线、双下划线》我们介绍过以前置双下划线开始,后置双下划线结束的方法名:__*__,这是系统定义的一批特殊函数,通常被称之为:魔法函数(Magic Methods 或 Dunder Methods),其中Dunder意为:“Double Under (Underscores)”。本文我们就对这些Dunder函数做一次系统介绍。
魔法函数并不会被直接调用,而在类的某些特殊行为发生时被自动调用。实际上,从其他语言观察Python的这些魔法函数,都有对应的解决方案,只是Python选择了这种“内置的预定义的函数”作为Python的处理方式。我个人倾向于把这些魔法函数分为:
- 对象生命周期相关的操作
 - 对象基本操作(属性,toString等)
 - 运算符重载
 - 集合相关操作
 - with上下文管理器
 - 协程相关
 

(注:上图引用自:https://zhuanlan.zhihu.com/p/344951719)
以下是各魔法方法的列表:
| 方法名 | 用途 | 
|---|---|
__init__ | Initialise object | 
__new__ | Create object | 
__del__ | Destroy object | 
__repr__ | Compute “official” string representation / repr(obj) | 
__str__ | Pretty print object / str(obj) / print(obj) | 
__bytes__ | bytes(obj) | 
__format__ | Custom string formatting | 
__lt__ | obj < … | 
__le__ | obj <= … | 
__eq__ | obj == … | 
__ne__ | obj != … | 
__gt__ | obj > … | 
__ge__ | obj >= … | 
__hash__ | hash(obj) / object as dictionary key | 
__bool__ | bool(obj) / define Truthy/Falsy value of object | 
__getattr__ | Fallback for attribute access | 
__getattribute__ | Implement attribute access: obj.name | 
__setattr__ | Set attribute values: obj.name = value | 
__delattr__ | Delete attribute: del obj.name | 
__dir__ | dir(obj) | 
__get__ | Attribute access in descriptor | 
__set__ | Set attribute in descriptor | 
__delete__ | Attribute deletion in descriptor | 
__init_subclass__ | Initialise subclass | 
__set_name__ | Owner class assignment callback | 
__instancecheck__ | isinstance(obj, …) | 
__subclasscheck__ | issubclass(obj, …) | 
__class_getitem__ | Emulate generic types | 
__call__ | Emulate callables / obj(*args, **kwargs) | 
__len__ | len(obj) | 
__length_hint__ | Estimate length for optimisation purposes | 
__getitem__ | Access obj[key] | 
__setitem__ | obj[key] = … or `obj[] | 
__delitem__ | del obj[key] | 
__missing__ | Handle missing keys in dict subclasses | 
__iter__ | iter(obj) / for … in obj (iterating over) | 
__reversed__ | reverse(obj) | 
__contains__ | … in obj (membership test) | 
__add__ | obj + … | 
__radd__ | … + obj | 
__iadd__ | obj += … | 
__sub__ | obj - … | 
__mul__ | obj * … | 
__matmul__ | obj @ … | 
__truediv__ | obj / … | 
__floordiv__ | obj // … | 
__mod__ | obj % … | 
__divmod__ | divmod(obj, …) | 
__pow__ | obj ** … | 
__lshift__ | obj << … | 
__rshift__ | obj >> … | 
__and__ | obj & … | 
__xor__ | obj ^ … | 
__or__ | obj | 
__neg__ | -obj (unary) | 
__pos__ | +obj (unary) | 
__abs__ | abs(obj) | 
__invert__ | ~obj (unary) | 
__complex__ | complex(obj) | 
__int__ | int(obj) | 
__float__ | float(obj) | 
__index__ | Losslessly convert to integer | 
__round__ | round(obj) | 
__trunc__ | math.trunc(obj) | 
__floor__ | math.floor(obj) | 
__ceil__ | math.ceil(obj) | 
__enter__ | with obj (enter context manager) | 
__exit__ | with obj (exit context manager) | 
__await__ | Implement awaitable objects | 
__aiter__ | aiter(obj) | 
__anext__ | anext(obj) | 
__aenter__ | async with obj (enter async context manager) | 
__aexit__ | async with obj (exit async context manager) | 
(注:更多关联到官方文档的解释,请参考:https://mathspp.com/blog/pydonts/dunder-methods#fn:2)
由于方法众多,我们可以使用下述方法来查看当前对象所有的函数,所有的魔法函数也会罗列出来。
dir(<your-object>)
 
由于魔法函数众多,我们不会在本文中一一解读,以后在使到的场景中再跟进做一些笔记。本文,我们只关注一部分最基本也最常见的魔法函数。
__init__()
 
总是第一个被提起的魔法函数,实际上就是Python的构造函数。该函数会在对象初始化的时由解释器自动调用,程序员可以自由选择实现或不实现,但就像其他编程语言中的构造函数一样,一般都会选择实现。如果不实现,则对象属性是不会被声明和初始化的,虽然Python是动态语言,在后续使用对象的过程中依然可以随时添加和初始化属性,但是从编程习惯上讲,还是在构造函数中显示声明出所有属性是更好的选择。
__new__()
 
在object类中存在一个静态的__new__(cls, *args, **kwargs) 方法,该方法需要传递一个参数 cls,cls 表示需要实例化的类,此参数在实例化时由 Python 解释器自动提供,__new__ 方法必须有返回值,且返回的是被实例化的实例,只有在该实例返回后才会调用__init__ 来进行初始化,初始化所用的实例就是__new__ 返回的结果,也就可以认为是 self。
__init__和__new__的区别
 
__new__的调用发生在__init__之前__new__是类级别的方法(方法参数是cls),__init__是实例级别的方法(方法参数是self)
当我们创建一个Python实例时,Python会先调用__new__来创建一个实例,然后再调用这个实例的__init__方法来完成一些实例属性的初始化操作。
绝大多数情况下, 我们只需要实现__init__方法,关于__new__有两种典型的应用场景:
当你继承一些不可变的class时(比如int, str, tuple), 提供给你一个自定义这些类的实例化过程的途径。
- 当我们要继承一些不可变类时,
__new__给了我们一个“调整”这些类的实例化细节的入口,例如如下代码: 
class PositiveInteger(int):
    def __new__(cls, value):
        return super(PositiveInteger, cls).__new__(cls, abs(value))
i = PositiveInteger(-3)
print(i)
 
由于Int是不可变类,继承自它的PositiveInteger会自动取绝对值作为其值。尽管该意图在init方法中同样可以实现,但是在类级别的new方法中实现是更合理的,应为是类型(即PositiveInteger)本身的固有特性。
- 通过
__new__实现单例 
class Single(object):
    _instance = None
    def __new__(cls, *args, **kw):
        if cls._instance is None:
            cls._instance = object.__new__(cls, *args, **kw)
        return cls._instance
    def __init__(self):
        pass
single1 = Single()
single2 = Single()
single1.attr1='value1'
print(single1.attr1)
print(single2.attr1)
print(id(single1) == id(single2))
 
程序输出:
value1
value1
True
 
基于new实现的单例能很好地体现new区别于init的地方。
__iter__()
 
只要定义了__iter__() 方法对象,就是可迭代对象;这意味着,我们可以迭代我们自己定义的对象。
__next__()
 
函数next() 是 iterator 区别于 iterable 的关键,它允许我们显式地获取一个元素。当调用 next() 方法时,实际上产生了 2 个操作:一是更新 iterator 状态,令其指向后一项,以便下一次调用,二是返回当前结果。
__call__()
 
对象通过提供__call__() 方法可以模拟函数的行为,如果一个对象提供了该方法,就可以像函数一样使用它。
__len__()
 
len 调用后会调用对象的__len__ 函数,我们可以为其定制输出。
__str__()
 
直接打印对象的实现方法,str 是被 print 函数调用的,一般都是 return 一个什么东西,这个东西应该是以字符串的形式表现的。如果不是要用 str() 函数转换,我们可以直接 print 的对象都是实现了__str__ 这个方法的,比如 dict。
__repr__()
 
函数 str() 用于将值转化为适于人阅读的形式,而 repr() 转化为供解释器读取的形式,某对象如果没有适于人阅读的解释形式的话,我们可以定制__repr__ 的输出。
__setitem__()
 
该函数可以给对象赋值,我们可以以下标的方式对其进行操作
__getitem__()
 
与上函数相反,getitem 可以使对象支持以下标的方式获取值。
__delitem__()
 
该函数支持以下标方式删除对象数据,实现了这三个函数,这个类就像字典一样,具备了基本的增删查功能,有时候这样写会很方便。
__del__()
 
这可以说是一个析构器,或者回收器,在对象引用数降到0时执行,有时可能还需要等一会再执行,所以一般不推荐使用,但是在代码中我们偶尔可以用它来实现一些必须要做的,但是并不紧急的事。
__setattr__()
 
该函数可以设置函数的属性。
__getattr__()
 
获取对象属性,只有在属性没有找到的时候调用。
__getattribute__()
 
该函数和上面介绍的__getattr__ 很像,都是获取属性,但是__getattr__ 是在属性不存在时被调用,而__getattribute__ 是无条件被调用,这样会方便我们做一些控制,需要注意,一旦定义 了__getattribute__,则__getattr__ 不再会被调用,除非显式调用。
##__delattr__()
 本函数的作用是删除属性,实现了该函数的类可以用 del 命令来删除属性。
参考:
https://www.cnblogs.com/chenhuabin/p/13752770.html
https://levelup.gitconnected.com/python-dunder-methods-ea98ceabad15
https://www.tutorialsteacher.com/python/magic-methods-in-python
https://mathspp.com/blog/pydonts/dunder-methods
https://lupanlpb.github.io/2019/08/14/Python%E5%B8%B8%E7%94%A8%E9%AD%94%E6%B3%95%E5%87%BD%E6%95%B0/










git已merge到master分支代码且被同事代码覆盖如何回退](https://img-blog.csdnimg.cn/e0c0c948ce6d400d89a3fb15eae00e8e.png)








