如何在 Python 中使用方法重载?

2025-02-07 08:44:00
admin
原创
65
摘要:问题描述:我正在尝试在 Python 中实现方法重载:class A: def stackoverflow(self): print ('first method') def stackoverflow(self, i): print ('second ...

问题描述:

我正在尝试在 Python 中实现方法重载:

class A:
    def stackoverflow(self):    
        print ('first method')
    def stackoverflow(self, i):
        print ('second method', i)

ob=A()
ob.stackoverflow(2)

但输出是second method 2;类似地:

class A:
    def stackoverflow(self):    
        print ('first method')
    def stackoverflow(self, i):
        print ('second method', i)

ob=A()
ob.stackoverflow()

给出

Traceback (most recent call last):
  File "my.py", line 9, in <module>
    ob.stackoverflow()
TypeError: stackoverflow() takes exactly 2 arguments (1 given)

我该如何让它工作?


解决方案 1:

这是方法重载,而不是方法覆盖。在 Python 中,你过去通常用一个函数完成所有操作:

class A:
    def stackoverflow(self, i='some_default_value'):
        print('only method')

ob=A()
ob.stackoverflow(2)
ob.stackoverflow()

请参阅Python 教程的默认参数值部分。请参阅“最不惊讶”和可变默认参数,了解应避免的常见错误。

有关 Python 3.4 中添加的单调度泛型函数的信息,请参阅PEP 443 :

>>> from functools import singledispatch
>>> @singledispatch
... def fun(arg, verbose=False):
...     if verbose:
...         print("Let me just say,", end=" ")
...     print(arg)
>>> @fun.register(int)
... def _(arg, verbose=False):
...     if verbose:
...         print("Strength in numbers, eh?", end=" ")
...     print(arg)
...
>>> @fun.register(list)
... def _(arg, verbose=False):
...     if verbose:
...         print("Enumerate this:")
...     for i, elem in enumerate(arg):
...         print(i, elem)

解决方案 2:

您也可以使用pythonlangutil:

from pythonlangutil.overload import Overload, signature

class A:
    @Overload
    @signature()
    def stackoverflow(self):    
        print('first method')
    
    @stackoverflow.overload
    @signature("int")
    def stackoverflow(self, i):
        print('second method', i)

解决方案 3:

虽然agf 在 3.4 之前的版本中给出了正确的答案,但现在有了PEP-3124,我们得到了语法糖。

有关装饰器的详细信息,请参阅类型文档@overload,但请注意,这实际上只是语法糖,恕我直言,这也是人们一直以来争论的焦点。

None就我个人而言,我同意使用具有不同签名的多个函数比使用具有 20 多个参数的单个函数(大多数情况下)更具可读性,然后不得不使用无休止的if, elif,else链来找出调用者实际上希望我们的函数使用提供的参数集做什么。这是 Python Zen 之后早就应该做的:

美丽比丑陋更好。

也可以说

简单比复杂更好。

直接来自上面链接的官方 Python 文档:

from typing import overload
@overload
def process(response: None) -> None:
    ...
@overload
def process(response: int) -> Tuple[int, str]:
    ...
@overload
def process(response: bytes) -> str:
    ...
def process(response):
    <actual implementation>

编辑:如果有人想知道为什么这个例子不能像你预期的那样在其他语言中工作,我建议看看这个讨论。这些@overloaded函数不应该有任何实际的实现。从 Python 文档中的示例中看不出这一点。

解决方案 4:

在 Python 中,您不会这样做。当人们在 Java 等语言中这样做时,他们通常想要一个默认值(如果没有,他们通常想要一个具有不同名称的方法)。因此,在 Python 中,您可以拥有默认值。

class A(object):  # Remember the ``object`` bit when working in Python 2.x

    def stackoverflow(self, i=None):
        if i is None:
            print 'first form'
        else:
            print 'second form'

如您所见,您可以使用它来触发单独的行为,而不仅仅是具有默认值。

>>> ob = A()
>>> ob.stackoverflow()
first form
>>> ob.stackoverflow(2)
second form

解决方案 5:

你不能、永远不需要、也不想这么做。

在 Python 中,一切都是对象。类是事物,所以它们是对象。方法也是如此。

有一个名为的对象A,它是一个类。它有一个名为的属性stackoverflow。它只能有一个这样的属性。

当你编写 时def stackoverflow(...): ...,实际上会创建一个对象,即方法,并将其分配给stackoverflow的属性A。如果你编写两个定义,则第二个定义将替换第一个,就像赋值始终表现的方式一样。

此外,您也不想编写代码来执行有时使用重载执行的更疯狂的事情。这不是语言的工作方式。

不要尝试为可能给出的每种事物定义一个单独的函数(这没有什么意义,因为您无论如何都不会为函数参数指定类型),不要再担心事物什么,而要开始思考它们能做什么

您不仅不能编写单独的程序来处理元组与列表,而且也不想或不需要这样做

您所做的只是利用它们都是可迭代的事实(例如,您可以编写for element in container:)。 (它们不直接通过继承相关这一事实无关紧要。)

解决方案 6:

我认为您要找的词是“重载”。 Python 中没有任何方法重载。 但是,您可以使用默认参数,如下所示。

def stackoverflow(self, i=None):
    if i != None:
        print 'second method', i
    else:
        print 'first method'

当你传递一个参数时,它会按照第一个条件的逻辑执行第一个打印语句。当你不传递任何参数时,它会进入条件else并执行第二个打印语句。

解决方案 7:

我用 Python 3.2.1 写了我的答案。

def overload(*functions):
    return lambda *args, **kwargs: functions[len(args)](*args, **kwargs)

工作原理:

  1. overload接受任意数量的可调用函数并将它们存储在元组中functions,然后返回 lambda。

  2. lambda 接受任意数量的参数,然后返回存储在functions[number_of_unnamed_args_passed]called 中的调用函数的结果,并将参数传递给 lambda。

用法:

class A:
    stackoverflow=overload(                    \n        None,  
        #there is always a self argument, so this should never get called
        lambda self: print('First method'),      \n        lambda self, i: print('Second method', i) \n    )

解决方案 8:

我用 Python 2.7 写了我的答案:

在 Python 中,方法重载是不可能的;如果您确实想要访问具有不同特性的相同函数,我建议您进行方法覆盖。

class Base(): # Base class
    '''def add(self,a,b):
        s=a+b
        print s'''

    def add(self,a,b,c):
        self.a=a
        self.b=b
        self.c=c

        sum =a+b+c
        print sum

class Derived(Base): # Derived class
    def add(self,a,b): # overriding method
        sum=a+b
        print sum



add_fun_1=Base() #instance creation for Base class
add_fun_2=Derived()#instance creation for Derived class

add_fun_1.add(4,2,5) # function with 3 arguments
add_fun_2.add(4,2)   # function with 2 arguments

解决方案 9:

python 3.5 添加了 typing 模块。其中包括一个重载装饰器。

此装饰器的目的是帮助类型检查器。从功能上讲,它只是鸭子类型检查器。

from typing import Optional, overload


@overload
def foo(index: int) -> str:
    ...


@overload
def foo(name: str) -> str:
    ...


@overload
def foo(name: str, index: int) -> str:
    ...


def foo(name: Optional[str] = None, index: Optional[int] = None) -> str:
    return f"name: {name}, index: {index}"


foo(1)
foo("bar", 1)
foo("bar", None)

这导致 VS Code 中出现以下类型信息:

vscode 中的结果

虽然这能有所帮助,但请注意,这会增加很多“奇怪”的新语法。其目的 - 纯粹的类型提示 - 并不是立即显而易见的。

采用类型联合通常是一个更好的选择。

解决方案 10:

在 Python 中,重载不是一个应用概念。但是,如果您尝试创建一种情况,例如,如果传递了 类型的参数,则执行一个初始化程序foo,而传递 类型的参数则执行另一个初始化程序bar,那么由于 Python 中的所有内容都作为对象处理,因此您可以检查传递的对象的类类型的名称,并根据该名称编写条件处理。

class A:
   def __init__(self, arg)
      # Get the Argument's class type as a String
      argClass = arg.__class__.__name__

      if argClass == 'foo':
         print 'Arg is of type "foo"'
         ...
      elif argClass == 'bar':
         print 'Arg is of type "bar"'
         ...
      else
         print 'Arg is of a different type'
         ...

这个概念可以根据需要通过不同的方法应用于多种不同的场景。

解决方案 11:

在 Python 中,您可以使用默认参数来执行此操作。

class A:

    def stackoverflow(self, i=None):    
        if i == None:
            print 'first method'
        else:
            print 'second method',i

解决方案 12:

我刚刚发现了loading.py(Python 3 的函数重载),有兴趣的可以看一下。

来自链接存储库的 README 文件:

重载是一个根据运行时参数的类型和数量提供函数调度的模块。

当调用重载函数时,调度程序会将提供的参数与可用的函数签名进行比较,并调用提供最准确匹配的实现。

特征

注册时的函数验证和详细的解析规则可确保运行时产生唯一且定义明确的结果。实现函数解析缓存,以获得出色的性能。支持函数签名中的可选参数(默认值)。在解析最佳匹配时评估位置参数和关键字参数。支持后备函数和共享代码的执行。支持参数多态性。支持类和继承,包括类方法和静态方法。

解决方案 13:

Python 不像 Java 或 C++ 那样支持方法重载。我们可以重载方法,但只能使用最新定义的方法。

# First sum method.
# Takes two argument and print their sum
def sum(a, b):
    s = a + b
    print(s)

# Second sum method
# Takes three argument and print their sum
def sum(a, b, c):
    s = a + b + c
    print(s)

# Uncommenting the below line shows an error
# sum(4, 5)

# This line will call the second sum method
sum(4, 5, 5)

我们需要提供可选参数或*args,以便在调用时提供不同数量的参数。

感谢Python | 方法重载

解决方案 14:

Python 3.x 包含标准类型库,允许使用 @overload 装饰器进行方法重载。不幸的是,这是为了使代码更具可读性,因为 @overload 装饰的方法后面需要跟一个处理不同参数的非装饰方法。更多信息可以在这里找到,但对于您的示例:

from typing import overload
from typing import Any, Optional
class A(object):
    @overload
    def stackoverflow(self) -> None:    
        print('first method')
    @overload
    def stackoverflow(self, i: Any) -> None:
        print('second method', i)
    def stackoverflow(self, i: Optional[Any] = None) -> None:
        if not i:
            print('first method')
        else:
            print('second method', i)

ob=A()
ob.stackoverflow(2)

解决方案 15:

有一些库可以使这变得简单:

functools- 如果你只需要第一个参数使用@singledispatch

plum-dispatch- 功能丰富的方法/函数重载。

multipledispatch- 功能较少但重量较轻的替代品plum

解决方案 16:

Python 在PEP-3124中添加了 @overload 装饰器,通过类型检查为重载提供语法糖 - 而不仅仅是覆盖。

PEP-3124 中通过 @overload 重载的代码示例

from overloading import overload
from collections import Iterable

def flatten(ob):
    """Flatten an object to its component iterables"""
    yield ob

@overload
def flatten(ob: Iterable):
    for o in ob:
        for ob in flatten(o):
            yield ob

@overload
def flatten(ob: basestring):
    yield ob

被@overload-decorator转换为:

def flatten(ob):
    if isinstance(ob, basestring) or not isinstance(ob, Iterable):
        yield ob
    else:
        for o in ob:
            for ob in flatten(o):
                yield ob

解决方案 17:

MathMethod.py文件中:

from multipledispatch import dispatch
@dispatch(int, int)
def Add(a, b):
   return a + b 
@dispatch(int, int, int)  
def Add(a, b, c):
   return a + b + c 
@dispatch(int, int, int, int)    
def Add(a, b, c, d):
   return a + b + c + d

Main.py文件中

import MathMethod as MM 
print(MM.Add(200, 1000, 1000, 200))

我们可以使用多重分派来重载该方法。

解决方案 18:

现在(3.11),您可以使用 typing 在 python 中进行重载。https ://docs.python.org/3/library/typing.html#typing.overload

以下是直接从给定的 URL 复制的。

@typing.overload
用于创建重载函数和方法的装饰器。

@overload 装饰器允许描述支持多种不同参数类型组合的函数和方法。一系列 @overload 装饰定义后面必须跟有一个非 @overload 装饰定义(针对同一个函数/方法)。

@overload 修饰的定义仅供类型检查器使用,因为它们将被非 @overload 修饰的定义覆盖。同时,非 @overload 修饰的定义将在运行时使用,但类型检查器应忽略它。在运行时,直接调用 @overload 修饰的函数将引发 NotImplementedError。

重载的示例,其提供的类型比使用联合或类型变量表达的更精确:

@overload
def process(response: None) -> None:
    ...
@overload
def process(response: int) -> tuple[int, str]:
    ...
@overload
def process(response: bytes) -> str:
    ...
def process(response):
    ...  # actual implementation goes here
相关推荐
  政府信创国产化的10大政策解读一、信创国产化的背景与意义信创国产化,即信息技术应用创新国产化,是当前中国信息技术领域的一个重要发展方向。其核心在于通过自主研发和创新,实现信息技术应用的自主可控,减少对外部技术的依赖,并规避潜在的技术制裁和风险。随着全球信息技术竞争的加剧,以及某些国家对中国在科技领域的打压,信创国产化显...
工程项目管理   1579  
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   1355  
  信创产品在政府采购中的占比分析随着信息技术的飞速发展以及国家对信息安全重视程度的不断提高,信创产业应运而生并迅速崛起。信创,即信息技术应用创新,旨在实现信息技术领域的自主可控,减少对国外技术的依赖,保障国家信息安全。政府采购作为推动信创产业发展的重要力量,其对信创产品的采购占比情况备受关注。这不仅关系到信创产业的发展前...
信创和国产化的区别   8  
  信创,即信息技术应用创新产业,旨在实现信息技术领域的自主可控,摆脱对国外技术的依赖。近年来,国货国用信创发展势头迅猛,在诸多领域取得了显著成果。这一发展趋势对科技创新产生了深远的推动作用,不仅提升了我国在信息技术领域的自主创新能力,还为经济社会的数字化转型提供了坚实支撑。信创推动核心技术突破信创产业的发展促使企业和科研...
信创工作   9  
  信创技术,即信息技术应用创新产业,旨在实现信息技术领域的自主可控与安全可靠。近年来,信创技术发展迅猛,对中小企业产生了深远的影响,带来了诸多不可忽视的价值。在数字化转型的浪潮中,中小企业面临着激烈的市场竞争和复杂多变的环境,信创技术的出现为它们提供了新的发展机遇和支撑。信创技术对中小企业的影响技术架构变革信创技术促使中...
信创国产化   8  
热门文章
项目管理软件有哪些?
云禅道AD
禅道项目管理软件

云端的项目管理软件

尊享禅道项目软件收费版功能

无需维护,随时随地协同办公

内置subversion和git源码管理

每天备份,随时转为私有部署

免费试用