卡码笔记-最强八股文
首页
计算机基础
Python
C++
Java
Go
🔥大模型🔥
  • 大模型面经
  • Java面经
  • C++面经
大模型Github (opens new window)
代码随想录 (opens new window)
首页
计算机基础
Python
C++
Java
Go
🔥大模型🔥
  • 大模型面经
  • Java面经
  • C++面经
大模型Github (opens new window)
代码随想录 (opens new window)
  • 本栏必读

    • Python面试题专栏介绍
    • Python学习路线
    • Python零基础入门课
  • 基础语法

  • 函数、作用域与模块

  • 面向对象

    • Python中的类和对象是什么?
    • 实例属性和类属性有什么区别?
    • 实例方法、类方法和静态方法有什么区别?
    • __new__和__init__有什么区别?
    • Python如何实现封装、继承和多态?
    • Python多继承的MRO是什么?super()如何工作?
    • Python常见魔术方法有哪些?
      • 简要回答
      • 详细回答
      • 知识拓展
  • 进阶语法与语言机制

# Python常见魔术方法有哪些?

面试官问:"Python的魔术方法有哪些?它们有什么用?"

很多人只知道__init__,但Python有几十个魔术方法。真正要讲清楚的是:魔术方法(双下划线方法,也叫dunder methods)让对象能支持特定语法和协议,比如运算符、索引、迭代、字符串表示、上下文管理等。 通过实现对应的魔术方法,你的自定义类可以像内置类型一样使用。

# 简要回答

魔术方法是以双下划线开头和结尾的特殊方法,如__init__、__str__、__add__。它们不应该直接调用,而是通过特定语法触发。

常见分类包括:

  • 对象创建和初始化:__new__、__init__、__del__
  • 字符串表示:__str__、__repr__
  • 运算符重载:__add__、__eq__、__lt__等
  • 容器协议:__len__、__getitem__、__setitem__、__iter__
  • 属性访问:__getattr__、__setattr__、__delattr__
  • 上下文管理:__enter__、__exit__
  • 可调用对象:__call__

实现魔术方法能让自定义类支持Python的语法糖,使代码更简洁、更符合Python风格。

# 详细回答

# 对象创建和生命周期

__new__(cls, ...)

创建对象,返回实例。通常不需要重写,除非要控制对象创建(单例、不可变类型)。

__init__(self, ...)

初始化对象,设置属性。最常用的魔术方法。

__del__(self)

对象被销毁时调用,但不可靠。Python的垃圾回收时机不确定,不要在__del__里做关键清理工作(用上下文管理器代替)。

class Resource:
    def __init__(self, name):
        self.name = name
        print(f"Resource {name} created")
    
    def __del__(self):
        print(f"Resource {self.name} deleted")

r = Resource("file")
del r  # Resource file deleted(可能延迟)
1
2
3
4
5
6
7
8
9
10

# 字符串表示

__str__(self)

str(obj)或print(obj)时调用,返回给人看的字符串。

__repr__(self)

repr(obj)或交互式环境显示对象时调用,返回给开发者看的字符串,理想情况下应该是能重建对象的表达式。

class Point:
    def __init__(self, x, y):
        self.x = x
        self.y = y
    
    def __str__(self):
        return f"Point at ({self.x}, {self.y})"
    
    def __repr__(self):
        return f"Point({self.x}, {self.y})"

p = Point(3, 4)
print(p)       # Point at (3, 4),调用__str__
print(repr(p)) # Point(3, 4),调用__repr__
1
2
3
4
5
6
7
8
9
10
11
12
13
14

最佳实践:至少实现__repr__。如果没有__str__,str(obj)会回退到__repr__。

# 运算符重载

比较运算符:

  • __eq__(self, other):==
  • __ne__(self, other):!=
  • __lt__(self, other):<
  • __le__(self, other):<=
  • __gt__(self, other):>
  • __ge__(self, other):>=
class Point:
    def __init__(self, x, y):
        self.x = x
        self.y = y
    
    def __eq__(self, other):
        return self.x == other.x and self.y == other.y
    
    def __lt__(self, other):
        return (self.x**2 + self.y**2) < (other.x**2 + other.y**2)

p1 = Point(1, 2)
p2 = Point(1, 2)
print(p1 == p2)  # True
print(p1 < Point(3, 4))  # True
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

算术运算符:

  • __add__(self, other):+
  • __sub__(self, other):-
  • __mul__(self, other):*
  • __truediv__(self, other):/
  • __floordiv__(self, other)://
  • __mod__(self, other):%
class Vector:
    def __init__(self, x, y):
        self.x = x
        self.y = y
    
    def __add__(self, other):
        return Vector(self.x + other.x, self.y + other.y)
    
    def __repr__(self):
        return f"Vector({self.x}, {self.y})"

v1 = Vector(1, 2)
v2 = Vector(3, 4)
print(v1 + v2)  # Vector(4, 6)
1
2
3
4
5
6
7
8
9
10
11
12
13
14

反向运算符(当左操作数不支持时):

  • __radd__、__rsub__等

就地运算符:

  • __iadd__、__isub__等,对应+=、-=

# 容器协议

__len__(self)

len(obj)时调用,返回容器长度。

__getitem__(self, key)

obj[key]时调用,返回元素。

__setitem__(self, key, value)

obj[key] = value时调用。

__delitem__(self, key)

del obj[key]时调用。

__contains__(self, item)

item in obj时调用。

class MyList:
    def __init__(self):
        self._items = []
    
    def __len__(self):
        return len(self._items)
    
    def __getitem__(self, index):
        return self._items[index]
    
    def __setitem__(self, index, value):
        self._items[index] = value
    
    def __contains__(self, item):
        return item in self._items
    
    def append(self, item):
        self._items.append(item)

ml = MyList()
ml.append(10)
ml.append(20)
print(len(ml))      # 2
print(ml[0])        # 10
ml[0] = 15
print(10 in ml)     # False
print(15 in ml)     # True
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

__iter__(self)

返回迭代器,支持for item in obj。

__next__(self)

迭代器的next()方法。

class CountDown:
    def __init__(self, start):
        self.current = start
    
    def __iter__(self):
        return self
    
    def __next__(self):
        if self.current <= 0:
            raise StopIteration
        self.current -= 1
        return self.current + 1

for i in CountDown(3):
    print(i)  # 3, 2, 1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

# 属性访问

__getattr__(self, name)

访问不存在的属性时调用。

__setattr__(self, name, value)

设置属性时调用。

__delattr__(self, name)

删除属性时调用。

__getattribute__(self, name)

访问任何属性时都调用(包括存在的),优先级高于__getattr__。

class DynamicAttrs:
    def __init__(self):
        self.data = {}
    
    def __getattr__(self, name):
        if name in self.data:
            return self.data[name]
        raise AttributeError(f"No attribute '{name}'")
    
    def __setattr__(self, name, value):
        if name == 'data':
            super().__setattr__(name, value)
        else:
            self.data[name] = value

obj = DynamicAttrs()
obj.x = 10
print(obj.x)  # 10
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

# 上下文管理

__enter__(self)

with语句进入时调用,返回值赋给as后的变量。

__exit__(self, exc_type, exc_val, exc_tb)

with语句退出时调用,处理异常和清理。

class FileManager:
    def __init__(self, filename, mode):
        self.filename = filename
        self.mode = mode
        self.file = None
    
    def __enter__(self):
        self.file = open(self.filename, self.mode)
        return self.file
    
    def __exit__(self, exc_type, exc_val, exc_tb):
        if self.file:
            self.file.close()
        return False  # 不抑制异常

with FileManager('test.txt', 'w') as f:
    f.write('Hello')
# 退出时自动关闭文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

# 可调用对象

__call__(self, ...)

让对象可以像函数一样调用。

class Multiplier:
    def __init__(self, factor):
        self.factor = factor
    
    def __call__(self, x):
        return x * self.factor

double = Multiplier(2)
print(double(5))  # 10
print(double(10)) # 20
1
2
3
4
5
6
7
8
9
10

# 其他常见魔术方法

__hash__(self)

返回对象的哈希值,支持hash(obj)和作为字典键。

__bool__(self)

bool(obj)或if obj时调用,返回布尔值。

__format__(self, format_spec)

format(obj, spec)或f-string的格式化。

常见魔术方法的分类和触发场景

# 知识拓展

Q1:魔术方法可以直接调用吗?

技术上可以,但不推荐。魔术方法是给解释器调用的:

# 不推荐
obj.__str__()

# 推荐
str(obj)
1
2
3
4
5

直接调用魔术方法会绕过一些检查和优化。

Q2:为什么叫魔术方法?

因为它们通过特殊语法"魔法般地"被调用。比如obj1 + obj2会自动调用obj1.__add__(obj2)。

Q3:如果不实现__eq__,==会怎样?

默认比较对象身份(地址),等价于is。两个属性相同的对象,用==比较会返回False。

Q4:__getattr__和__getattribute__有什么区别?

  • __getattribute__:访问任何属性都调用,包括存在的属性。
  • __getattr__:只有属性不存在时才调用。

重写__getattribute__要小心,容易进入无限递归:

class Test:
    def __getattribute__(self, name):
        # 错误:会递归
        # return self.name
        
        # 正确:调用父类的__getattribute__
        return super().__getattribute__(name)
1
2
3
4
5
6
7

Q5:有多少个魔术方法?

几十个。常用的包括本文列出的这些,完整列表在Python官方文档 (opens new window)。不需要全部记住,根据需求查文档即可。


上一篇:Python多继承的MRO是什么?super()如何工作?

到此,面向对象这一章的7篇文章全部完成。这些文章覆盖了类与对象、属性、方法、对象创建、封装继承多态、MRO和魔术方法,形成完整的Python面向对象知识体系。

Last Updated: 10/13/2026, 5:57:00 PM

← Python多继承的MRO是什么?super()如何工作? 可迭代对象和迭代器有什么区别? →

评论

验证登录状态...

侧边栏 侧边栏
夜间模式 夜间
卡码简历 卡码简历
代码随想录 代码随想录
卡码投递表 卡码投递表🔥
2026实习校招群 2026群
添加客服微信 2026实习校招客服微信 PS:通过微信后,请发送姓名-学校-年级-2026实习/校招
支持卡码笔记 支持卡码笔记
鼓励/支持/赞赏Carl 卡码笔记赞赏码
1. 如果感觉本站对你很有帮助,也可以请Carl喝杯奶茶,金额大小不重要,心意已经收下
2. 希望大家都能梦想成真,有好的前程,加油💪