在Python 中，魔术方法指的是以双下划线 `__` 开头和结尾的特殊方法。这些方法提供了一种在类中定义特殊行为的方式，可以让我们自定义 Python 中的内置行为。下面是一些常用的魔术方法：

1. `__init__`: 初始化方法，用于创建类实例时初始化对象的属性。
2. `__str__`: 字符串表示方法，用于返回类实例的字符串表示。
3. `__repr__`: 表示方法，一般用于打印实例的一些属性，如果没有实现`__str__`，则调用这个。
4. `__eq__`: 等于方法，用于判断两个对象是否相等。
5. `__lt__`: 小于方法，用于比较两个对象的大小。
6. `__len__`: 长度方法，用于返回对象的长度。
7. `__getitem__`: 索引方法，用于支持类实例的索引操作。
8. `__setitem__`: 赋值方法，用于支持类实例的赋值操作。
9. `__delitem__`: 删除方法，用于支持类实例的删除操作。
10. `__call__`: 调用方法，用于支持将类实例作为函数调用。
11. `__enter__` 和 `__exit__`: 上下文管理器方法，用于支持 `with` 语句。
12. `__getattr__`: 获取属性方法，用于在访问不存在的属性时触发。
13. `__setattr__`: 设置属性方法，用于在设置属性时触发。
14. `__delattr__`: 删除属性方法，用于在删除属性时触发。
15. `__iter__`: 迭代方法，用于支持类实例的迭代操作。
16. `__next__`: 下一个方法，用于支持类实例的迭代操作。

****

17、`__class__ `和` __module`__ ：`__class__ `和` __module`__ 都是 Python 中的魔术属性，用于获取对象所属的类和模块。

`__class__ `属性用于获取对象所属的类，它是一个只读属性。例如，如果有一个对象 obj，可以使用 `obj.__class__ `来获取它所属的类。这个属性通常用于实现类似于装饰器或者代理的功能，可以在运行时动态地修改类的行为。

`__module__ `属性用于获取对象所属的模块名称，它也是一个只读属性。例如，如果有一个对象 obj，可以使用 `obj.__module__ `来获取它所属的模块名称。这个属性通常用于在不同的模块之间共享对象，可以根据模块名称动态地引用对象。

下面是一个示例，展示了如何使用 __class__ 和 __module__ 属性来获取对象所属的类和模块：

```python
class MyClass:
    pass
obj = MyClass()

print(obj.__class__)  # 输出：<class '__main__.MyClass'>
print(obj.__module__)  # 输出：__main__
```

在上面的示例中，我们定义了一个 MyClass 类，并创建了一个对象 obj。通过调用` obj.__class__` 和 `obj.__module__ `属性，我们可以获取对象 obj 所属的类和模块。在这个例子中，`obj.__class`__ 的返回值是 `<class '__main__.MyClass'>`，表示 obj 属于 MyClass 类；`obj.__module`__ 的返回值是` __main`__，表示 obj 所在的模块是当前模块。

需要注意的是，访问魔术属性通常是不必要的，因为 Python 提供了许多内置函数和语法糖来获取对象的类和模块。例如，可以使用 type(obj) 来获取对象的类，可以使用 `obj.__class__.__name__` 来获取对象所属的类名，可以使用 `__name__ `属性来获取模块名称。



这些魔术方法可以让我们自定义类的行为，使其与 Python 的内置类型一样灵活。需要注意的是，不是所有的魔术方法都需要在类中实现，只有在需要自定义特殊行为时才需要实现相应的魔术方法。