python基础之单分派泛函数singledispatch

 更新时间:2023年08月14日 09:53:40   作者:易辰_  
这篇文章主要介绍了python基础之单分派泛函数singledispatch问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教

python单分派泛函数singledispatch

singledispatch 是标准库 functools 模块的函数

可以把整体方案拆成多个模块,甚至可以为你无法修改的类提供专门的函数,使用 @singledispatch 装饰的函数会变成泛函数

1、 singledispatch :标记处理object类型的基函数

2、各个专门函数使用 @<<base_function>>.register(<<type>>) 装饰

3、专门函数的名称无关紧要, _ 是个不错的选择,简单明了

4、为每个需要处理的类型注册一个函数5、可以叠放多个 register 装饰器,让同一个函数支持不同类型

函数中使用

我们来看一个例子了解下:

from functools import singledispatch
from collections import  abc
@singledispatch
def show(obj):
    print (obj, type(obj), "obj")
#参数字符串
@show.register(str)
def _(text):
    print (text, type(text), "str")
#参数int
@show.register(int)
def _(n):
    print (n, type(n), "int")
#参数元祖或者字典均可
@show.register(tuple)
@show.register(dict)
def _(tup_dic):
    print (tup_dic, type(tup_dic), "int")
show(1)
show("xx")
show([1])
show((1,2,3))
show({"a":"b"})

输出如下:

1 <class 'int'> int
xx <class 'str'> str
[1] <class 'list'> obj
(1, 2, 3) <class 'tuple'> int
{'a': 'b'} <class 'dict'> int

好处是什么呢?类似于java的重载机制,可以在一个类中为同一个方法定义多个重载变体,比在一个函数中使用一长串的 if/elif

对象中使用

我们来看一个对象的例子

from functools import singledispatch
class abs:
    def type(self,args):
        ""
class Person(abs):
    @singledispatch
    def type(self,args):
        super().type("",args)
        print("我可以接受%s类型的参数%s"%(type(args),args))
    @type.register(str)
    def _(text):
        print("str",text)
    @type.register(tuple)
    def _(text):
        print("tuple", text)
    @type.register(list)
    @type.register(dict)
    def _(text):
        print("list or dict", text)
Person.type("safly")
Person.type((1,2,3))
Person.type([1,2,3])
Person.type({"a":1})
Person.type(Person,True)

输出如下:

str safly
tuple (1, 2, 3)
list or dict [1, 2, 3]
list or dict {'a': 1}
我可以接受<class 'bool'>类型的参数True

python的singledispatch装饰器

最近一直在学习装饰器的相关知识,学习到了functools中的singledispatch装饰器,记录一下

1.Python中不需要使用函数重载的原因

Python中一般是不需要使用函数的重载的。一般的静态语言例如C#是支持函数的重载的,为了就是多态以及代码的重用。

例如我们现在想要实现一个函数,它可以输出输入参数的类型,用C#函数的重载实现的代码如下

static void GetType(string input)
{
    Console.WriteLine("{0}是string类型", input);
}
static void GetType(int input)
{
    Console.WriteLine("{0}是int类型", input);
}
static void GetType(string[] input)
{
    Console.WriteLine("{0}是数组类型", input);
}
static void GetType(Dictionary<string,string> input)
{
    Console.WriteLine("{0}是字典类型", input);
}

此时如果想使用Python实现则不需要使用函数的重载,因为Python本身就是动态语言,不要在函数的参数中指定参数的类型,可以直接在函数体中判断变量的类型并且执行相应的语句即可:

def print_type(obj):
    """输出参数obj的类型"""
    if isinstance(obj, int):
        print(f"{obj}的类型是int")
    elif isinstance(obj, str):
        print(f"{obj}的类型是str")
    elif isinstance(obj, list):
        print(f"{obj}的类型是list")
    elif isinstance(obj, dict):
        print(f"{obj}的类型是dict")
    else:
        print(f"{obj}是其他类型")

2.Python中的泛函数以及singledispatch

上面简单的代码虽然使用Python也实现了功能,但是如果功能再复杂一点则我们需要写更多的if-elif语句,并且如果函数需要根据参数的类型来执行对应的子函数的话此时代码会更臃肿,并且不利于功能的扩展。

为此,在Python3.4以后在标准库中functools中加入了singledispatch装饰器。被singledispatch装饰的函数称为泛函数,由此实现的也被称为单分派泛函数。

其实在Python的标准库中就存在泛函数,例如len(),它就会根据传入参数的类型去读取相应的C结构体中对象的长度。但是在Python3.4以前用户是没有办法实现类似Pythonic的泛函数的。

并且有时候当使用不是自己编写的或者是无法修改的类的时候,我们需要向其中添加我们自定义的函数,此时就很难做到。但是有了singledispatch之后就会变得很容易。

下面为使用泛函数实现上述代码功能:

from functools import singledispatch
from collections import abc
import numbers
@singledispatch
def print_type_new(obj):  
    pass
@print_type_new.register(numbers.Integral)  
def _(n): 
    print(f"{n}的类型是Integral")
@print_type_new.register(str)
def _(text):
    print(f"{text}的类型是str")
@print_type_new.register(tuple)
@print_type_new.register(abc.MutableSequence)
def _(text):
    print(f"{text}的类型是Sequence")
@print_type_new.register(abc.Mapping)
def _(text):
    print(f"{text}的类型是Mapping")

使用singledispatch的时候,首先需要装饰一个基函数f(一般参数类型为object),之后需要为各个专门的函数使用类似于@f.register(type)之类的装饰器,并且要为每个需要特殊处理的类型注册一个函数,同时为了代码的兼容性,该专门函数应该尽可能的只处理抽象基类而不要处理具体实现(FluentPython P172)。

注意:

在Python如果需要根据传入参数的类型来让函数执行不同的操作,则此时应该将函数写成泛函数,即使用singledispatch装饰器。

注意singledispatch并不是重载,因为重载还有类似于参数类型相同,但是数量不同,这种类似的情况在Python中只需要使用可变参数*即可以解决。

singledispatch的引入本质是为了让用户可以自定义泛函数,以便可以处理在需要根据参数的类型做出相同操作的场合以及为第三方类添加自定义的函数,这一切都是为了提高程序的可扩展性。

总结

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

相关文章

  • python 遍历列表提取下标和值的实例

    python 遍历列表提取下标和值的实例

    今天小编就为大家分享一篇python 遍历列表提取下标和值的实例,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2018-12-12
  • pytorch  网络参数 weight bias 初始化详解

    pytorch 网络参数 weight bias 初始化详解

    这篇文章主要介绍了pytorch 网络参数 weight bias 初始化详解,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2020-06-06
  • python使用openpyxl库读取Excel文件数据

    python使用openpyxl库读取Excel文件数据

    openpyxl是一个功能强大的库,可以轻松地实现Excel文件的读写操作,本文将介绍如何使用openpyxl库读取Excel文件中的数据,感兴趣的小伙伴可以了解下
    2023-11-11
  • pip install如何指定包的安装路径

    pip install如何指定包的安装路径

    最近学习python需要用pip下载一些包,但是发现下载后在pycharm中根本导入不了,下面这篇文章主要给大家介绍了关于pip install如何指定包的安装路径的相关资料,文中通过实例代码介绍的非常详细,需要的朋友可以参考下
    2022-06-06
  • python脚本打包后无法运行exe文件的解决方案

    python脚本打包后无法运行exe文件的解决方案

    这篇文章主要介绍了python脚本打包后无法运行exe文件的解决方案,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2021-03-03
  • 如何用tempfile库创建python进程中的临时文件

    如何用tempfile库创建python进程中的临时文件

    这篇文章主要介绍了如何用tempfile库创建python进程中的临时文件,帮助大家更好的理解和使用python,感兴趣的朋友可以了解下
    2021-01-01
  • 使用Python的Supervisor进行进程监控以及自动启动

    使用Python的Supervisor进行进程监控以及自动启动

    这篇文章主要介绍了使用Python的Supervisor进行进程监控以及自动启动,使用python supervisor实现,需要的朋友可以参考下
    2014-05-05
  • Python 中 key 参数的含义及用法小结

    Python 中 key 参数的含义及用法小结

    我们在使用 sorted() 或 map() 函数的时候,都会看到里面有一个 key 参数,其实这个 key 参数也存在于其他内置函数中(例如 min()、max() 等),那么我们今天就来了解一下 key 参数的含义以及用途吧,需要的朋友可以参考下
    2023-12-12
  • TensorFlow 合并/连接数组的方法

    TensorFlow 合并/连接数组的方法

    今天小编就为大家分享一篇TensorFlow 合并/连接数组的方法,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2018-07-07
  • Python实现K-近邻算法的示例代码

    Python实现K-近邻算法的示例代码

    k-近邻算法(K-Nearest Neighbour algorithm),又称 KNN 算法,是数据挖掘技术中原理最简单的算法。本文将介绍实现K-近邻算法的示例代码,需要的可以参考一下
    2022-09-09

最新评论