Quark's Outlines:Python 基础定制
Source: Dev.to
请提供您希望翻译的具体文本内容(文章正文),我将把它翻译成简体中文并保留原有的格式、Markdown 语法以及代码块和链接不变。谢谢!
Source: …
概览、历史时间线、问题与解决方案
当你在 Python 中创建一个类时,可以改变对象在某些常见情形下的行为。这称为 Python 基础自定义。Python 为你提供了以双下划线开头和结尾的 特殊方法 名称。这些方法让你能够控制对象在以下情况下会发生什么:
- 创建时
- 显示(打印)时
- 比较时
- 删除时
- 真值测试时
不要自行调用这些方法——Python 会在恰当的时机调用它们。你只需要定义它们的行为即可。
示例:__init__ 和 __str__
class Greeter:
def __init__(self, name):
self.name = name
def __str__(self):
return f"Hello, {self.name}!"
g = Greeter("Ada")
print(g)
# prints:
# Hello, Ada!
这里,Python 在对象创建时调用 __init__,在对象被打印时调用 __str__。
常见的特殊(魔法)方法
| 方法 | 何时运行 |
|---|---|
__init__ | 对象创建 |
__del__ | 对象删除(当引用计数降至零时) |
__str__ | print() 或 str() |
__repr__ | repr() 或交互式解释器显示 |
__cmp__ | Python 2:通用比较 |
__eq__, __lt__, __le__, __gt__, __ge__, __ne__ | Python 3:丰富比较 |
__hash__ | 对象用作字典键或集合元素时 |
__bool__(在 Python 2 中为 __nonzero__) | 真值测试(if obj:) |
你可以在类中定义上述任意方法,以让对象以更有用的方式表现。
使用魔法方法挂钩核心操作
class Number:
def __init__(self, value):
self.value = value
def __bool__(self):
return self.value != 0
print(bool(Number(0))) # False
print(bool(Number(5))) # True
该对象根据其内部值告诉 Python 应该将其视为 True 还是 False。
Python 的自定义方法来源何处?
| Year | Milestone |
|---|---|
| 1980 | Smalltalk 和 C++ 引入了用于构造、析构和字符串表示的特殊方法名。 |
| 1989 | Python 的类模型采用了 __init__、__del__ 和 __str__。 |
| 1991 | Python 0.9.0 添加了 __cmp__ 和 __repr__ 用于比较和调试视图。 |
| 1995 | 真值测试加入了 __nonzero__(后在 Python 3 中被 __bool__ 替代)。 |
| 2001 | 在 Python 2.2 中细化了 __hash__ 规则,以实现可预测的字典行为。 |
| 2008 | Python 3.0 移除了 __cmp__,改用富比较方法(__eq__、__lt__ 等)。 |
| 2025 | 核心方法名称已稳定;近期的更改仅关注性能或内部使用。 |
Source: …
如何正确使用 Python 基础定制
下面列出了一些常见问题及其解决方案,每个示例都展示了特定的魔法方法。
1. 在对象创建时运行代码
问题: 你希望在对象实例化的瞬间保存名称,而不必在之后再调用其他方法。
解决方案: 定义 __init__。
class User:
def __init__(self, name):
self.name = name
u = User("Ada")
print(u.name) # prints: Ada
2. 在打印对象时显示可读的字符串
问题: 直接打印实例会得到类似 “ 的输出。你想要一个简洁、友好的表示。
解决方案: 定义 __str__。
class Report:
def __init__(self, title):
self.title = title
def __str__(self):
return f"Report: {self.title}"
r = Report("Q2 Sales")
print(r) # prints: Report: Q2 Sales
3. 在对象销毁时清理资源
问题: 对象使用了文件或网络资源,需要在对象不再需要时确保其关闭。
解决方案: 定义 __del__。
class Connection:
def __del__(self):
print("Connection closed.")
c = Connection()
del c # prints: Connection closed.
注意: 单独依赖
__del__可能不够可靠(例如循环引用)。若需确定的清理,建议使用上下文管理器(with语句)以及__enter__/__exit__协议。
4. 比较对象
问题: 你有一个表示商品及其价格的类,需要对它们进行比较。
解决方案: 在 Python 3 中,实现所需的富比较方法(如 __lt__、__eq__ 等)。
class Item:
def __init__(self, price):
self.price = price
def __lt__(self, other):
return self.price < other.price
a = Item(10)
b = Item(15)
print(a < b) # prints: True
如果需要完整的排序功能,可以继承自 functools.total_ordering,并实现 __eq__ 加上任意一个其他比较方法。
5. 将对象用作字典键
问题: 你希望自定义类的实例能够作为字典的键,这要求哈希值稳定且与相等性保持一致。
解决方案: 一致地实现 __hash__(以及 __eq__)。
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
def __eq__(self, other):
if not isinstance(other, Point):
return NotImplemented
return (self.x, self.y) == (other.x, other.y)
def __hash__(self):
return hash((self.x, self.y))
p1 = Point(1, 2)
p2 = Point(1, 2)
my_dict = {p1: "origin"}
print(my_dict[p2]) # prints: origin
摘要
Python 的 魔法方法(即带有双下划线的那些)让你可以挂接到语言的核心操作——对象创建、表示、比较、真值测试、哈希以及清理。通过在类中定义相应的方法,你可以让对象在日常 Python 代码中表现得自然直观。
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 __hash__(self):
return hash((self.x, self.y))
p = Point(1, 2)
d = {p: "here"}
print(d[p])
# prints:
# here
Python 调用 __hash__ 来确定对象在字典中应存放的位置。
Mike Vincent 是一位来自加利福尼亚州洛杉矶的美国软件工程师和应用开发者。了解更多关于 Mike Vincent 的信息。