pyhon之重载&&多态

前言

在一些动态语言中,大都存在一个重载的概念。这是在OOP(面向对象编程)中的一个必不可少的行为。

所谓重载,就是多个函数名相同的函数,根据传入的参数个数、阐述类型从而执行不同的功能。所谓重载实质上是为了解决编程中参数可变不统一的问题

Python中的重载

在python中,具有重载的思想却没有重载的概念。所以有的人你说python这门语言并不支持函数重载,有的人说python具有重载功能。实际上python编程中具有重载的目的却没有重载的行为,或者说python并不需要重载!

python是一门动态语言,不需要声明变量类型,函数中可以接受任何类型的参数也就无法根据参数类型来支持重载,python没有必要去考虑参数的类型问题,这些都可以在函数内部去判断处理,并无必要去重新编写一个函数。

python有多种传参方式:默认参数/可变参数/可变关键字参数,多种传参方式可以处理函数参数中参数可变的问题。

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}是其他类型")

Python中的泛函数以及singledispatch装饰器

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

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

posted @ 2022-01-25 12:45  习久性成  阅读(178)  评论(0编辑  收藏  举报