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


169

我正在尝试在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)

我该如何工作?


39
在Python中,将方法视为一组特殊的“ 属性 ”,并且对象的给定名称只能有一个“ 属性 ”(因此只有一个方法)。最后一个方法将覆盖以前的所有方法。在Java中,方法不是一等公民(它们不是“对象的属性”),而是由“发送消息”调用的,这些消息是根据最接近的类型静态解析的(这是重载的来源)。



6
为什么尚未接受该问题的答案?只需单击您最喜欢的答案左侧的多余的复选标记...
glglgl 2012年

Answers:


150

它是方法重载而不是方法重写。在Python中,您可以通过一个函数来完成全部操作:

class A:

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

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

在Python中,不能有两个具有相同名称的方法,而且也不需要。

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

编辑:有关Python 3.4中新的单调度通用功能的信息,请参阅PEP 443


119
而且您不需要 -恕我直言,有时像C ++这样的方法重载将非常方便。好的,从某种意义上说,它不是“需要”的,因为它不能使用其他结构来完成-但这会使某些事情变得更加容易和简单。
Andreas Florath 2012年

7
@AndreasFlorath我不同意。学会热爱鸭子的输入并编写每种方法,这样它就只能做一件事,并且不需要方法重载。
2012年

4
+1是让我在被抓住之前阅读有关“避免的常见错误”的信息
mdup,2013年

43
我想稍微不同意;)...重载通常会使代码更干净,因为您不会在方法中包含太多if-else语句来处理不同的情况。从某种意义上说,功能语言的整个范围都使用类似的思想,即参数模式匹配。这意味着您将拥有更小的更清洁的方法。
sten

2
@ user1019129这就是在Python 3.4中添加并链接到我的答案中的单个调度通用函数的目的。
agf

62

您还可以使用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

6
我认为这是对该问题的唯一有效答案。如果可以的话,我会加倍投票。
迈克尔(Michael)2010年

3
很好,但不适用于原始函数,仅适用于类中的方法。
Legit Stack

1
@LegitStack也可以添加该功能。这不是不可能的。
Ehsan Keshavarzian

3
@LegitStack我在GitHub上更新了代码,现在它也可以使用函数。
Ehsan Keshavarzian

1
@PaulPrice是的。我更新了答案,并删除了官方支持部分。您仍然可以使用我的代码调度重载。现在,它可以同时使用方法和功能。从GitHub获取代码。我尚未更新PyPi。
Ehsan Keshavarzian

48

在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

2
None当您需要可变的默认值时,通常很有用。单独的行为应在单独的功能中进行。
2012年

@agf:None也可以用作真正的默认值。
克里斯·摩根

是的,但是我指的是将其用作哨兵值,这就是您在答案中使用它的方式,并且我认为我的评论很清楚。
2012年

你说“一般”?您是否暗示并非总是如此?
joel

24

您可以,永远也不需要,也不需要。

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

有一个称为A类的对象。它具有称为的属性stackoverflow。它只能具有一个这样的属性。

在编写时def stackoverflow(...): ...,发生的事情是创建一个方法对象,并将其分配给的stackoverflow属性A。如果您编写两个定义,则第二个定义将替换第一个定义,即分配始终起作用。

此外,您也不想编写执行有时用于重载的各种事情的代码。这不是这种语言的工作方式。

不要试图为每种类型的事物定义一个单独的函数(这毫无意义,因为您始终不为函数参数指定类型),而不必担心事物什么而开始思考它们可以做什么

您不仅不能编写一个单独的元组来处理一个元组还是一个列表,而且不想要也不需要

您要做的就是利用它们都是可迭代的事实(例如,您可以编写for element in container:)。(它们与继承没有直接关系这一事实无关紧要。)


6
TBH,“永远不需要”会更加小心。可以在任何现实世界的每项功能上标记并经过完整的编程语言标记的东西,因此不是有效的参数。谁需要发电机?谁需要上课?编程语言只是更具体的东西的语法糖。
塞巴斯蒂安·马赫

6
完全不同意。可能是您“不需要”或“从未想要”,但是有许多应用程序是您迫切想要的。尝试如把手Python和优雅numpy的阵列,而不具有的instanceof的乱抛垃圾程序......,写一个程序
艾玛·詹德

1
根据masi的回答,我会说“您不能”现在是不正确和过时的。基于@overload装饰器的存在,我会说“真的不想要”充其量是有争议的。在PEP-3124中,“ ...目前,它是Python代码检查接收到的参数类型的常用反模式...实现此目的的'明显方式'是通过类型检查,但是这种方法很脆弱并且不适合扩展...”看来好像有足够的人愿意,它成为Python的一部分。
Mike S

@MikeS,标准@overload仅用于键入。
纳凡纳尔

@Narfanar我不知道您的回复如何适用于我的评论。你能解释一下吗?
迈克S

16

虽然@agf过去使用PEP-3124正确地回答了问题,但我们得到了语法糖。有关装饰器的详细信息 ,请参见键入文档。@overload但是请注意,这实际上只是语法糖,恕我直言,这是所有人们从那时以来一直在争论的话题。我个人认为,具有不同特征的多个功能,使它更具可读性则具有20+的参数都设置为默认值(单个功能None的大部分时间),然后不得不反复折腾使用不已ifelifelse链找出什么调用者实际上希望我们的函数处理提供的参数集。在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>

正是我想要的东西,比定义自己的重载装饰器更
整齐

2
BTW:任何人都奇怪,为什么这不工作,我建议采取看看讨论stackoverflow.com/questions/52034771/... -该@overload版功能不应该有任何实际的实现。从Python文档中的示例来看,这并不明显。
玛西

15

我用Python 3.2.1写出答案。

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

这个怎么运作:

  1. overload接受任意数量的可调用对象并将其存储在tuple中functions,然后返回lambda。
  2. lambda接受任意数量的参数,然后返回存储在functions[number_of_unnamed_args_passed]被调用中的调用函数的结果,并带有传递给lambda的参数。

用法:

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

14

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

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

当您传递参数时,它将遵循第一个条件的逻辑并执行第一个print语句。当您不传递任何参数时,它将进入else条件并执行第二个print语句。


13

我用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中,重载不是一个可应用的概念。但是,如果您试图创建一种情况,例如,如果要传递一个类型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'
         ...

根据需要,可以通过不同的方法将此概念应用于多个不同的方案。


7

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

class A:

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

5

刚刚遇到这个https://github.com/bintoro/overloading.py的人可能感兴趣。

从链接的存储库的自述文件中:

重载是一个基于运行时参数的类型和数量提供功能分派的模块。

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

特征

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


3

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,以便在调用时提供不同数量的args。

来自https://www.geeksforgeeks.org/python-method-overloading/


这不是过载。这就是所谓的覆盖。Python支持后一种。第一个可以用装饰器实现。
Paebbels

2

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)

1
答案结尾处的“ The”使我认为您尚未完成答案的编写。请编辑您的答案以完成它。
Artjom B.

0

在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))

我们可以通过使用multidispatch重载方法


这需要使用不是Python核心一部分的multipledispatch软件包(pypi.org/project/multipledispatch)。
安东尼

0

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
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.