Python 的 Magic Method
    在 Python 中,所有以 ““ 双下划线包起来的方法,都统称为”魔术方法”。比如我们接触最多的 **init__ 。使用这些魔术方法,我们可以构造出优美的代码,将复杂的逻辑封装成简单的方法。
    我们可以使用 Python 内置的方法
    dir()来列出类中所有的魔术方法.示例如下↓
    image.png输出的结果为
    image.pngimage.pngimage.pngimage.png
    共26种由此可以看到,一个类的魔术方法还是挺多的,不过我们只需要了解一些常见和常用的魔术方法就好了

    构造(new)和初始化(init)
    实际上,创建一个类的过程是分为两步的,一步是创建类的对象,还有一步就是对类进行初始化。
    new是用来创建类并返回这个类的实例, 而init只是将传入的参数来初始化该实例.new 在创建一个实例的过程中必定会被调用,但 init 就不一定,比如通过 pickle.load 的方式反序列化一个实例时就不会调用 init方法。
    defnew(cls)是在 definit(self) 方法之前调用的,作用是返回一个实例对象。还有一点需要注意的是:new** 方法总是需要返回该类的一个实例,而 init** 不能返回除了None**的任何值,以下为实例↓

    1. class User(object):
    2. def __new__(cls, *args, **kwargs):
    3. # 打印 __new__方法中的相关信息
    4. print('调用了 def __new__ 方法')
    5. print(args)
    6. # 最后返回父类的方法
    7. return super(User, cls).__new__(cls)
    8. def __init__(self, name, age):
    9. print('调用了 def __init__ 方法')
    10. self.name = name
    11. self.age = age
    12. if __name__ == '__main__':
    13. usr = User('两点水', 23)
    1. <br />属性的访问控制<br />Python 没有真正意义上的私有属性。然后这就导致了对 Python 类的封装性比较差。我们有时候会希望 Python 能够定义私有属性,然后提供公共可访问的 **get** 方法和 **set** 方法。Python 其实可以通过魔术方法来实现封装<br />![image.png](https://cdn.nlark.com/yuque/0/2020/png/1662829/1593869379179-2d37d37a-1860-4359-b8d9-f82912f593cb.png#align=left&display=inline&height=818&margin=%5Bobject%20Object%5D&name=image.png&originHeight=818&originWidth=868&size=96300&status=done&style=none&width=868)<br />以下是上面各种属性控制的示例↓
    1. class User(object):
    2. def __getattr__(self, name):
    3. print('调用了 __getattr__ 方法')
    4. return super(User, self).__getattr__(name)
    5. def __setattr__(self, name, value):
    6. print('调用了 __setattr__ 方法')
    7. return super(User, self).__setattr__(name, value)
    8. def __delattr__(self, name):
    9. print('调用了 __delattr__ 方法')
    10. return super(User, self).__delattr__(name)
    11. def __getattribute__(self, name):
    12. print('调用了 __getattribute__ 方法')
    13. return super(User, self).__getattribute__(name)
    14. if __name__ == '__main__':
    15. user = User()
    16. # 设置属性值,会调用 __setattr__
    17. user.attr1 = True
    18. # 属性存在,只有__getattribute__调用
    19. user.attr1
    20. try:
    21. # 属性不存在, 先调用__getattribute__, 后调用__getattr__
    22. user.attr2
    23. except AttributeError:
    24. pass
    25. # __delattr__调用
    26. del user.attr1

    其最后输出的结果为

    1. 调用了 __setattr__ 方法
    2. 调用了 __getattribute__ 方法
    3. 调用了 __getattribute__ 方法
    4. 调用了 __getattr__ 方法
    5. 调用了 __delattr__ 方法
    1. <br />对象的描述器<br />一个描述器是一个有“绑定行为”的对象属性** (object attribute)**,它的访问控制被描述器协议方法重写<br />这些方法是 **__get__()**, **__set__()** , 和 **__delete__()**.默认对属性的访问控制是从对象的字典里面 **(__dict__)** 中获取 **(get)** , 设置 **(set)** 和删除 **(delete).**举例来说, **a.x** 的查找顺序是, **a.__dict__['x']**, 然后**type(a).__dict__['x']** , 然后找**type(a)**的父类 ( 不包括元类 (metaclass) ).如果查找到的值是一个描述器, Python 就会调用描述器的方法来重写默认的控制行为**有这些方法的对象叫做描述器,下面就是一段带有对象描述器的示例代码**
    1. class User(object):
    2. def __init__(self, name='两点水', sex='男'):
    3. self.sex = sex
    4. self.name = name
    5. def __get__(self, obj, objtype):
    6. #获取 name 值
    7. return self.name
    8. def __set__(self, obj, val):
    9. #设置 name 值
    10. self.name = val
    11. class MyClass(object):
    12. x = User('两点水', '男')
    13. y = 5if __name__ == '__main__':
    14. m = MyClass()
    15. print(m.x)
    16. m.x = '三点水'
    17. print(m.x)
    18. print(m.x)
    19. print(m.y)

    输出的结果为

    1. // 两点水
    2. // 三点水
    3. // 三点水
    4. // 5

    通过这个例子,可以很好的观察到这get()set()这些方法的调用
    下面是一个经典的例子,由类来定义米和英尺两个属性

    1. class Meter(object):
    2. def __init__(self, value=0.0):
    3. self.value = float(value)
    4. def __get__(self, instance, owner):
    5. return self.value
    6. def __set__(self, instance, value):
    7. self.value = float(value)
    8. class Foot(object):
    9. def __get__(self, instance, owner):
    10. return instance.meter * 3.2808 def __set__(self, instance, value):
    11. instance.meter = float(value) / 3.2808class Distance(object):
    12. meter = Meter()
    13. foot = Foot()
    14. if __name__ == '__main__':
    15. d = Distance()
    16. print(d.meter, d.foot)
    17. d.meter = 1 print(d.meter, d.foot)
    18. d.meter = 2 print(d.meter, d.foot)

    输出的结果为

    1. 0.0 0.0
    2. 1.0 3.2808
    3. 2.0 6.5616
    1. 自定义容器<br />在 Python 中,常见的容器类型有: dict, tuple, list, string。其中也提到过可容器和不可变容器的概念。其中 tuple, string 是不可变容器,dict, list 是可变容器。可变容器和不可变容器的区别在于,不可变容器一旦赋值后,不可对其中的某个元素进行修改。然而,这些容器在我们有些特殊要求时并不足够与满足我们的要求,此时就有了自定义容器↓<br />![image.png](https://cdn.nlark.com/yuque/0/2020/png/1662829/1593872605773-590feb74-bc66-436e-a430-2d0f0cc61284.png#align=left&display=inline&height=786&margin=%5Bobject%20Object%5D&name=image.png&originHeight=786&originWidth=1061&size=120432&status=done&style=none&width=1061)<br />下面是使用上面魔术方法实现 Haskell 语言中的一个数据结构
    1. class FunctionalList:
    2. ''' 实现了内置类型list的功能,并丰富了一些其他方法: head, tail, init, last, drop, take'''
    3. def __init__(self, values=None):
    4. if values is None:
    5. self.values = []
    6. else:
    7. self.values = values
    8. def __len__(self):
    9. return len(self.values)
    10. def __getitem__(self, key):
    11. return self.values[key]
    12. def __setitem__(self, key, value):
    13. self.values[key] = value
    14. def __delitem__(self, key):
    15. del self.values[key]
    16. def __iter__(self):
    17. return iter(self.values)
    18. def __reversed__(self):
    19. return FunctionalList(reversed(self.values))
    20. def append(self, value):
    21. self.values.append(value)
    22. def head(self):
    23. # 获取第一个元素
    24. return self.values[0]
    25. def tail(self):
    26. # 获取第一个元素之后的所有元素
    27. return self.values[1:]
    28. def init(self):
    29. # 获取最后一个元素之前的所有元素
    30. return self.values[:-1]
    31. def last(self):
    32. # 获取最后一个元素
    33. return self.values[-1]
    34. def drop(self, n):
    35. # 获取所有元素,除了前N个
    36. return self.values[n:]
    37. def take(self, n):
    38. # 获取前N个元素
    39. return self.values[:n]
    1. <br />运算符相关的魔术方法<br />**比较运算符![image.png](https://cdn.nlark.com/yuque/0/2020/png/1662829/1593872984345-21e6c09f-8026-4a68-a239-03d820205b8a.png#align=left&display=inline&height=726&margin=%5Bobject%20Object%5D&name=image.png&originHeight=726&originWidth=1062&size=97799&status=done&style=none&width=1062)**<br />**算术运算符![image.png](https://cdn.nlark.com/yuque/0/2020/png/1662829/1593873021954-2ca0e475-333e-45d0-800a-086b94861150.png#align=left&display=inline&height=803&margin=%5Bobject%20Object%5D&name=image.png&originHeight=803&originWidth=613&size=49365&status=done&style=none&width=613)**