引言
说到函数重载,学过 Java 的同学应该不陌生,最常用的地方应该就是打印 log
了,对于不同的参数,调用的是不同的重载函数。那么 Python 如何实现函数重载呢?
重载概念
函数重载是指在同一作用域内,允许多个同名函数存在,但它们的参数列表不同。虽然许多编程语言(如 Java 和 C++)支持函数重载,但 Python 的设计哲学使其不能直接支持这一特性。
不使用重载
先看一个例子,在不使用重载的情况下,实现一个 log
函数:
from attr import dataclass
@dataclass
class MyException(Exception):
msg: str
def log(message):
if isinstance(message, MyException):
print(message.msg)
elif isinstance(message, str):
print(message)
else:
print(f'invalid message:{message}')
log(MyException('my exception'))
log('str exception')
log(1111)
# 运行结果为:
# my exception
# str exception
# invalid message:1111
不使用重载的话,就要写很多判断的代码,来判断入参的类型。
使用重载
from functools import singledispatch
from attr import dataclass
@dataclass
class MyException(Exception):
msg: str
@singledispatch
def log(message):
print(f'invalid message:{message}')
@log.register
def _(message: MyException):
print(message.msg)
@log.register
def _(message: str):
print(message)
log(MyException('my exception'))
log('str exception')
log(1111)
# 运行结果为:
# my exception
# str exception
# invalid message:1111
通过以上代码可以看到,使用重载的情况下,代码简洁了很多,将之前的 if-else
判断都去掉了,每个重载函数根据对应的类型直接输出对应的日志,说明在调用函数时,会自动判断函数的参数类型,然后调用对应的重载函数来执行对应的逻辑。
如果有新增类型的需求,只需要在原有的基础上增加一个重载函数即可,大大简化了新增类型的难度。例如:
from functools import singledispatch
from attr import dataclass
@dataclass
class MyException(Exception):
msg: str
@singledispatch
def log(message):
print(f'invalid message:{message}')
@log.register
def _(message: MyException):
print(message.msg)
@log.register
def _(message: str):
print(message)
@log.register
def _(message: int):
print(f'int message:{message}')
log(MyException('my exception'))
log('str exception')
log(1111)
# 运行结果为:
# my exception
# str exception
# int message:1111
重载写法
通过以上的代码,总结 Python 通过 functools.singledispatch
进行重载的写法。
- 首先定义一个函数,加上
@singledispatch
装饰器 - 然后添加几个以下划线为函数名的函数,因为重载函数的名字都一样,没有必要起其他的名字,用下划线代替即可。
- 为下划线函数设置对应的函数参数类型,编写函数内容。
- 调用函数来测试是否生效。
数据类
可能会有小伙伴问,自定义异常上有一个 @dataclass
装饰器,这个是干嘛用的,为什么没有写 __init__()
函数。
这个装饰器是Python3.6 中新引入的一个概念,熟悉 Java 的小伙伴可能会知道,它有点类似于 Java 中的 lombok 中的 @data
注解。它的作用是自动为用户自定义的类添加生成的特殊方法,例如 __init__()
和 __repr__()
。
from dataclasses import dataclass
@dataclass
class InventoryItem:
"""Class for keeping track of an item in inventory."""
name: str
unit_price: float
quantity_on_hand: int = 0
def total_cost(self) -> float:
return self.unit_price * self.quantity_on_hand
上面的类不需要单独再写下面的函数,@dataclass
装饰器会自动生成。
def __init__(self, name: str, unit_price: float, quantity_on_hand: int = 0):
self.name = name
self.unit_price = unit_price
self.quantity_on_hand = quantity_on_hand
所以 @dataclass
可以帮助我们简化代码,提升开发效率,具体的用法可以参考官方文档。
总结
在 Python 中,虽然不支持传统的函数重载,但我们可以通过 functools.singledispatch
方法来实现类似的功能。同时,需要注意一下,只有第一个参数的不同类型会被重载,后面参数的类型变化会被忽略。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。