Python中高级知识实现函数重载

更新时间:2023-06-02 15:28:52 阅读: 评论:0

Python中⾼级知识实现函数重载
函数的重载
在Java中,函数是有函数重载这个语⾔语法级功能的,因为Java是强类型语⾔,对于函数名相同,⼊参类型不同的功能⾼度⼀致的函数,就可以使⽤函数重载
Python是弱类型的语⾔,对函数的⼊参类型没有进⾏类型检查,也就没有函数重载这个概念。那么如果我们要做到类似于强类型语⾔的函数重载,应该怎么做。答案在Python⾃带的functools⾥⾯
样例
假设你有⼀个函数my_sum,它有⼀个参数param,这个参数可能是⼀个字符串,也可能是⼀个元组。例如:
print(my_sum('1,2'))
油酥千层饼print(my_sum((1, 2)))
你想在代码⾥⾯兼容这两种写法,于是你可能会这样写代码:
# !/usr/bin/env python3
# -*- coding: utf-8-*-
def my_sum(param) -> int:
if isinstance(param, str):
a, b = (int(x) for x in param.split(','))
return a + b
elif isinstance(param, tuple):
a, b = param
return a + b
el:
print("unsupported param type")
print(my_sum('1,2'))
print(my_sum((1, 2)))
这种写法简单直接,但是如果参数的类型更多,那么你就需要写很长的-el。代码看起来就⾮常不美观。学习过Java的同学,应该对函数重载⽐较熟悉,可以定义⼏个名字相同的函数,但是他们的参数类型或者数量不同,从⽽实现不同的代码逻辑。在 Python ⾥⾯,参数的数量不同可以使⽤默认参数来解决,不需要定义多个函数。那如果参数类型不同就实现不同的逻辑,除了上⾯的if-el外,我们还可以使⽤functools模块⾥⾯的singledispatch装饰器实现函数重载。
# !/usr/bin/env python3
# -*- coding: utf-8-*-
from functools import singledispatch
# 默认使⽤这个
# 不被⽀持的类型,返回0
@singledispatch
def my_sum(param) -> int:
print(f'传输参数类型为:{type(param)},不是有效类型')
return0
# 字符串使⽤它
@ister
def _(param: str):
a, b = (int(x) for x in param.split(','))
return a + b
# tuple使⽤它
@ister
def _(param: tuple):
a, b = param
海南三亚旅游return a + b
print(my_sum(1))
print(my_sum('1,2'))
print(my_sum((1, 2)))
输出:
传输参数类型为:<class'int'>,不是有效类型
3
3
可以看到,我们调⽤的函数,始终都是my_sum,但是由于传⼊参数的类型不同,它运⾏的结果也不⼀样。
我们使⽤singledispatch装饰⼀个函数,那么这个函数就是我们将会调⽤的函数。
这个函数在传⼊参数不同时的具体实现,通过下⾯注册的函数来实现。注册的时候使⽤@我们定义的函数名.register来注册。被注册的函数名叫什么⽆关紧要,所以这⾥我都直接使⽤下划线代替。
被注册的函数的第⼀个参数,通过类型标注来确定它应该使⽤什么类型。当我们调⽤我们定义的函数是,如果参数类型符合某个被注册的函数,那么就会执⾏这个被注册的函数。如果参数类型不满⾜任何⼀个被注册的函数,那么就会执⾏我们的原函数。
使⽤类型标注来指定参数类型是从 Python 3.7才引⼊的新特性。在 Python 3.6或之前的版本,我们需要通过@我们定义的函数名.register(类型)来指定类型,例如:
from functools import singledispatch
@singledispatch
def my_sum(param):
print(f'传输参数类型为:{type(param)},不是有效类型')
return0
@ister(str)
def _(param):
a, b = (int(x) for x in param.split(','))
蓝藻是植物吗
return a + b
@ister(tuple)
def _(param):
a, b = param
return a + b
冬季吃什么养生同时,还有⼀个需要注意的点,就是只有第⼀个参数的不同类型会被重载。后⾯的参数的类型变化会被⾃动忽略。龟兔赛跑
代码分析
原理很简单:
1. singledispatch(func) 将函数做⼀次装饰,并返回装饰后的函数,我叫它 wappered_func,wappered_func 实际上是singledispatch⾥
⾯的wrapper(*args, **kw)处理过的。
2. wappered_func 是func被包装后的函数,有了registry = {}存放到时注册进来的重构函数,有register⽅法来注册重构函数进registry,有
dispatch在执⾏函数是合理的更具函数⼊参类型绝顶registry⾥⾯的那么注册函数去处理⼊参
总结就是2步完成python模拟实现函数重构,本质还是采⽤设计模式中的适配器模式,其实和Java等的函数的重构有区别
python的函数的重构有⼀定弱点:
1. ONLY can have different behaviours depending upon the type of its FIRST argument
2. 只是对函数重构的动作的模仿,在功能上⼀致,其实并未做函数类型的检查和约束
源码
def singledispatch(func):
"""Single-dispatch generic function decorator.
Transforms a function into a generic function, which can have different
behaviours depending upon the type of its first argument. The decorated
function acts as the default implementation, and additional
implementations can be registered using the register() attribute of the
generic function.
"""
# There are many programs that u functools without singledispatch, so we
# trade-off making singledispatch marginally slower for the benefit of
# making start-up of such applications slightly faster.
import types, weakref
registry = {}
dispatch_cache = weakref.WeakKeyDictionary()
cache_token = None
def dispatch(cls):
"""generic_func.dispatch(cls) -> <function implementation>
Runs the dispatch algorithm to return the best available implementation        for the given *cls* registered on *generic_func*.
"""
nonlocal cache_token
赞怎么画
if cache_token is not None:
current_token = get_cache_token()
if cache_token != current_token:
dispatch_cache.clear()
cache_token = current_token
try:
impl = dispatch_cache[cls]
except KeyError:
try:
impl = registry[cls]
except KeyError:
impl = _find_impl(cls, registry)
dispatch_cache[cls] = impl
return impl
def register(cls, func=None):
"""ister(cls, func) -> func
Registers a new implementation for the given *cls* on a *generic_func*.
"""
nonlocal cache_token
if func is None:
if isinstance(cls, type):
return lambda f: register(cls, f)
ann = getattr(cls, '__annotations__', {})
if not ann:
rai TypeError(
f"Invalid first argument to `register()`: {cls!r}. "
f"U either `@register(some_class)` or plain `@register` "
f"on an annotated function."
)
func = cls
# only import typing if annotation parsing is necessary
古代婚礼from typing import get_type_hints
argname, cls = next(iter(get_type_hints(func).items()))
asrt isinstance(cls, type), (
f"Invalid annotation for {argname!r}. {cls!r} is not a class."
)
registry[cls] = func
if cache_token is None and hasattr(cls, '__abstractmethods__'):
cache_token = get_cache_token()
dispatch_cache.clear()
return func
def wrapper(*args, **kw):
if not args:
rai TypeError(f'{funcname} requires at least '
'1 positional argument')
return dispatch(args[0].__class__)(*args, **kw)
funcname = getattr(func, '__name__', 'singledispatch function')
registry[object] = func
wrapper.dispatch = dispatch
wrapper._clear_cache = dispatch_cache.clear
update_wrapper(wrapper, func)
return wrapper
演讲的英语

本文发布于:2023-06-02 15:28:52,感谢您对本站的认可!

本文链接:https://www.wtabcd.cn/fanwen/fan/82/834132.html

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。

标签:函数   类型   参数   注册   实现   重载   重构
相关文章
留言与评论(共有 0 条评论)
   
验证码:
推荐文章
排行榜
Copyright ©2019-2022 Comsenz Inc.Powered by © 专利检索| 网站地图