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如何使用标准库tmpfile库创建临时文件

    Python如何使用标准库tmpfile库创建临时文件

    这篇文章主要介绍了Python如何使用标准库tmpfile库创建临时文件问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2024-02-02
  • python字符串定义的三种方式

    python字符串定义的三种方式

    在Python中,字符串是一个非常重要的数据类型,可用来存储和操作文本数据,本文主要介绍了python字符串定义的三种方式,具有一定的参考价值,感兴趣的可以了解一下
    2023-05-05
  • 三分钟python搭建支付宝三方支付

    三分钟python搭建支付宝三方支付

    本文主要介绍了三分钟python搭建支付宝三方支付,文中通过示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2021-12-12
  • py3nvml实现GPU相关信息读取的案例分析

    py3nvml实现GPU相关信息读取的案例分析

    这篇文章主要介绍了py3nvml实现GPU相关信息读取,此时就可以考虑使用py3nvml这样的工具,针对于GPU任务执行的过程进行细化的分析,有助于提升GPU的利用率和程序执行的性能,需要的朋友可以参考下
    2022-01-01
  • python中torch.load中的map_location参数使用

    python中torch.load中的map_location参数使用

    在PyTorch中,torch.load()函数是用于加载保存模型或张量数据的重要工具,map_location参数为我们提供了极大的灵活性,具有一定的参考价值,感兴趣的可以了解一下
    2024-03-03
  • 如何在C++中调用Python

    如何在C++中调用Python

    虽然现在Python编程语言十分的火爆,但是实际上非要用一门语言去完成所有的任务,并不是说不可以,而是不合适。在一些特定的、对于性能要求比较高的场景,还是需要用到传统的C++来进行编程的。本文将用C++的代码去调用Python函数中实现的一些功能
    2021-05-05
  • Python数学建模StatsModels统计回归之线性回归示例详解

    Python数学建模StatsModels统计回归之线性回归示例详解

    这篇文章主要为大家介绍了Python数学建模中StatsModels统计回归之线性回归的示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助
    2021-10-10
  • Python中表格插件Tabulate的用法小结

    Python中表格插件Tabulate的用法小结

    这篇文章主要介绍了Python中表格插件Tabulate的用法,Tabulate插件是一个功能强大、简单易用的数据可视化工具,它能够满足我们在Python中进行表格数据展示的各种需求,通过使用Tabulate插件,我们能够轻松地生成美观且易读的表格,需要的朋友可以参考下
    2023-11-11
  • Python 脚本死锁问题与解决方案

    Python 脚本死锁问题与解决方案

    在脚本中,可以创建一个队列来存储子进程的输出,然后由主进程从队列中读取输出并进行处理,这篇文章主要介绍了Python 脚本死锁问题与解决方案,需要的朋友可以参考下
    2024-06-06
  • 利用Python统计每天敲了多少次键盘

    利用Python统计每天敲了多少次键盘

    每到年末各大App都会给你来一次年度总结,最近突发奇想,键盘是每天必备的工具,为啥不给键盘也来个工作总结,本文就来用Python统计一下每天敲了多少次键盘吧
    2024-04-04

最新评论