重载过载
Overloaded Overloading

原始链接: https://powerfulpython.com/blog/overloaded-overloading/

Python 区分了两种类型的“重载”。它不支持**方法重载**(即像 Java 那样定义多个具有不同签名的方法),在 Python 中定义同一个方法两次只会覆盖前者。 然而,Python 支持**运算符重载**。这使得像 `+` 这样的运算符可以根据输入类型表现出不同的行为(例如,数字相加或字符串拼接)。这是通过“魔术方法”(例如 `__add__`)来实现的。通过在自己的类中实现这些特殊钩子,你可以自定义标准运算符如何与你的对象交互。 如果你需要方法重载的功能,可以手动实现。这涉及创建一个“分发”方法,利用逻辑(如 `isinstance()` 检查)根据提供的参数将请求路由到相应的内部方法。若想采用更简洁的方式,Python 的 `functools` 模块提供了 `@singledispatch` 装饰器,允许你根据参数类型注册不同的实现。

Hacker News 最新 | 过往 | 评论 | 提问 | 展示 | 招聘 | 提交 登录 过载的重载 (powerfulpython.com) 6 分,由 redsymbol 发布于 2 小时前 | 隐藏 | 过往 | 收藏 | 讨论 | 帮助 考虑申请 YC 2026 秋季班!申请开放至 7 月 27 日。 准则 | 常见问题 | 列表 | API | 安全 | 法律 | 申请 YC | 联系 搜索:
相关文章

原文

"If Python doesn't support overloading, how does '+' work which can be either addition or concatenation, correct?"

My collegue Peter asked me this once. Let me explain this question, then answer it. And it starts with the word "overloading", which has two different meanings in Python.

When you write a class in Python, you can have only one version of each method. For example, this doesn't work:

  1. import math

  2. class Point:

  3. def __init__(self, x, y):

  4. self.x, self.y = x, y

  5. def distance(self, other_point):

  6. # Distance from this point to another point

  7. return math.sqrt(

  8. (self.x-other_point.x)**2 +

  9. (self.y-other_point.y)**2 )

  10. def distance(self, x, y):

  11. # Distance from this point to other x-y coords

  12. return math.sqrt( (self.x-x)**2 + (self.y-y)**2 )

See the two distance() methods? See how it's defined twice?

In Python, only the second version is kept. The first one is silently overwritten and erased. If you call Point.distance() with one argument - matching the first distance(), but not the second - you will get an error.

But if you translate this class to Java, the situation is different. Java lets you define both versions of distance(), and Java will use one or the other, depending on how many arguments you pass in. This is called "method overloading".

But Python doesn't let you do that. Peter knew this, and that's why he wondered about the "+" operator - and why Python lets you use it for different operations. Doesn't that conflict with Python's "no overloading" rule?

The answer is that while Python does not support method overloading...

It DOES support operator overloading.

And that's why you can use "+" for both strings and numbers, and in fact you can make it work with your own classes as well - using "magic method" hooks. For the "+" operator, we do that by defining a method called __add__. For example, in Point:

  1. class Point:

  2. def __init__(self, x, y):

  3. self.x, self.y = x, y

  4. # ...

  5. def __add__(self, other):

  6. return Point(self.x + other.x, self.y + other.y)

This "__add__" method can be defined on any of your classes. When you do, it lets you add instances of them together:

>>> p = Point(1,1) + Point(2,2)
>>> print(p.x, p.y)
3 3

If you check, you'll find both float() and str() have their own __add__ methods. And they implement addition and string concatenation, respectively.

See how this works? When we're talking about programming languages, "Overloading" refers to two different things. And Python does one of them, but not the other.

(You might say the term "overloaded" is... overloaded.)

P.S. Going back to the Point class - what would you do if you really need overloading for the distance() method?

Answer: There's two ways to do it. The more general method is to implement the method overloading yourself, by making distance() a dispatching method. That means its job is not to make any calculations directly; but rather determine which method should handle it, and then call it.

Typically you do this with generic argument names, and checking types with isinstance(), or the presence/absence of certain arguments:

  1. import math

  2. class Point:

  3. def __init__(self, x, y):

  4. self.x, self.y = x, y

  5. def distance(self, first, second=None):

  6. if isinstance(first, Point):

  7. # distance to other Point

  8. assert second is None

  9. return self.distance_to_other(first)

  10. elif isinstance(first, tuple):

  11. # distance to (x, y) tuple

  12. assert second is None

  13. return self.distance_to_pair(first)

  14. else:

  15. # distance to coordinates

  16. assert second is not None

  17. return self.distance_to_coordinates(first, second)

  18. def distance_to_coordinates(self, x, y):

  19. 'Distance from this point to other x-y coordinates'

  20. return math.sqrt( (self.x-x)**2 + (self.y-y)**2 )

  21. def distance_to_other(self, other_point):

  22. 'Distance from this point to another point'

  23. return self.distance_to_coordinates(

  24. other_point.x, other_point.y)

  25. def distance_to_pair(self, pair):

  26. 'Distance from this point to (x, y) tuple'

  27. x, y = pair

  28. return self.distance_to_coordinates(x, y)

The second and generally better way is to use the @singledispatch decorator, in Python's functools module. That lets you cleanly register different "dispatch" methods based on the type of the first argument.

That doesn't work with the Point class here, though, because its dispatching logic is too complex. But if you ever need to do something like this, try to use @singledispatch first.

联系我们 contact @ memedata.com