【问题标题】:How do I use method overloading in Python?如何在 Python 中使用方法重载?
【发布时间】:2012-04-29 11:36:32
【问题描述】:

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

我该如何进行这项工作?

【问题讨论】:

  • 在 Python 中,将方法视为一组特殊的“attributes”,并且只能有一个“attribute”(因此只有一个方法)的给定名称的对象。最后一个方法覆盖任何以前的方法。在 Java 中,方法不是一等公民(它们不是“对象的属性”),而是通过“发送消息”来调用,这些消息基于最接近的类型(这是 重载进来)。
  • 为什么这个问题的答案还没有被接受?只需单击您最喜欢的答案左侧的异常复选标记...

标签: python class overloading


【解决方案1】:

这是方法重载,而不是方法覆盖。而在 Python 中,您可以在一个函数中完成所有操作:

class A:

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

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

在 Python 中不能有两个同名的方法——而且你也不需要。

请参阅 Python 教程的 Default Argument Values 部分。请参阅 "Least Astonishment" and the Mutable Default Argument 了解要避免的常见错误。

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

【讨论】:

  • 而且你不需要 - 恕我直言,有时方法重载会非常方便,例如在 C++ 中。好的,从某种意义上说,它不是“需要”的,因为它不能使用其他构造来完成 - 但它会使一些事情变得更容易和更简单。
  • @AndreasFlorath 我不同意。学会爱上鸭子类型,编写每个方法,让它只做一件事,不需要方法重载。
  • +1 让我在被抓之前了解了“要避免的常见错误”
  • 我有点不同意 ;) ...重载通常会使代码更简洁,因为您不会使用太多 if-else 语句来处理不同的情况。从某种意义上说,整个函数式语言都使用类似的想法,即参数模式匹配。这意味着您将拥有更小更清洁的方法......而不是巨大的不可读的方法。
  • @agf:我所说的“厚脸皮”是指“它不存在,因为它不需要”之类的答案在读者看来是不屑一顾的,这通常是不合理的。
【解决方案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

【讨论】:

  • 我认为这是对这个问题的唯一有效答案。如果可以的话,我会加倍投票。
  • 这很好,但它不适用于原始函数,只能用于类中的方法。
  • @LegitStack 这个功能也可以加。这并非不可能。
  • @LegitStack 我在 GitHub 上更新了代码,现在它也可以使用函数了。
  • @PaulPrice 没错。我更新了我的答案并删除了官方支持部分。你仍然可以使用我的代码来调度重载。它现在适用于方法和函数。从 GitHub 获取代码。我还没有更新 PyPi。
【解决方案3】:

在 Python 中,您不会那样做。当人们在像 Java 这样的语言中这样做时,他们通常想要一个默认值(如果他们不这样做,他们通常想要一个具有不同名称的方法)。所以,在 Python 中,you can have default values.

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

【讨论】:

  • 当你想要一个可变的默认值时,None 通常很有用。单独的行为应该在单独的函数中。
  • @agf: None 也可以用作真正的默认值。
  • 是的,但我指的是使用它作为哨兵值,这就是你在答案中使用它的方式,我认为我的评论很清楚。
  • 你说“一般”?您的意思是并非总是如此?
  • 不,人们希望函数具有不同的行为,而不是具有“默认值”。不同参数类型和参数数量的行为略有不同。
【解决方案4】:

虽然agf was right with the answer in the past, pre-3.4,现在有了PEP-3124,我们得到了我们的语法糖。

请参阅 @overload 装饰器上的 typing documentation for details,但请注意,这实际上只是语法糖,恕我直言,这是人们一直以来争论的焦点。

就我个人而言,我同意拥有多个具有不同签名的函数使其更具可读性,然后将具有 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>

编辑:如果有人想知道为什么这个例子没有像你期望的那样工作,我建议看看this discussion@overloaded 函数不应该有任何实际实现。这在 Python 文档中的示例中并不明显。

【讨论】:

  • 正是我想要的,比定义自己的重载装饰器更整洁
  • 疯了,必须滚动至少 5 个“你不能那样做!!”要做到这一点,实际上是对问题的回答。谢谢@masi!
  • @th3coop 问题和较早的答案有多老,这在某种程度上是可以预料的。当时,答案确实是“你可以通过 hack 做到这一点,但你可能不应该”。一旦标准库中包含了一种实际的方法,就很容易推荐它。我知道 StackOverflow 正在研究一种按照最相关的答案进行排序的方法,而不仅仅是按照最有时间积累投票的答案。
【解决方案5】:

你不能,永远不需要,也不想这样做。

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

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

当您编写def stackoverflow(...): ... 时,您会创建一个作为方法的对象,并将其分配给Astackoverflow 属性。如果你写了两个定义,第二个会替换第一个,就像赋值总是一样。

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

不要尝试为可能给定的每种类型的东西定义一个单独的函数(这没有什么意义,因为你没有为函数参数指定类型),不要担心是什么东西 并开始思考他们可以做什么

您不仅不能单独编写一个来处理元组与列表,而且不想或不需要

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

【讨论】:

  • TBH,我会更加小心“永远不需要”。这是可以标记在任何现实世界和图灵完备编程语言的每个特征上的东西,因此不是一个有效的论点。谁需要生成器?谁需要类?编程语言只是更具体的语法糖。
  • 完全不同意。可能是您“从不需要”或“从不想”,但有足够多的应用程序是您非常想要的。尝试例如编写一个可以优雅地处理 Python 和 numpy 数组的程序,而不会用 instanceof 的...
  • 根据 masi 的回答,我会说“你不能”现在不正确且已过时。基于 @overload 装饰器的存在,我会说“真的不想”是有争议的,充其量。从 PEP-3124 开始,“......目前,Python 代码检查接收参数的类型是一种常见的反模式......执行此操作的“明显方法”是通过类型检查,但这很脆弱并且不适合扩展...”所以似乎有足够多的人想要,它成为 Python 的一部分。
  • @MikeS ,标准的@overload 仅用于打字。
  • @Narfanar 我不知道您的回复如何适用于我的评论。你能解释一下吗?
【解决方案6】:

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

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

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

【讨论】:

    【解决方案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] 中的调用函数的结果,该函数使用传递给 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) \
        )
    

    【讨论】:

      【解决方案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 中,重载不是一个应用概念。但是,如果您尝试创建一个案例,例如,您希望在传递 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'
                 ...
        

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

        【讨论】:

          【解决方案10】:

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

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

          【讨论】:

            【解决方案11】:

            我刚刚遇到overloading.py(Python 3 的函数重载),供任何可能感兴趣的人参考。

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

            overloading 是一个提供基于 运行时参数的类型和数量。

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

            特点

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

            【讨论】:

              【解决方案12】:

              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 | Method Overloading

              【讨论】:

              • 这不是超载。这称为覆盖。后者由 Python 支持。第一个可以用装饰器来实现。
              【解决方案13】:

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

              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)
              

              【讨论】:

              • 答案末尾的“The”让我觉得你还没有写完答案。请edit您的回答完成。
              【解决方案14】:

              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
              

              【讨论】:

              • 这样的东西真的很棒,但这只是 PEP-3124 中的一个提议,它处于“延迟”状态。它尚未在任何版本的 Python 中实现且不可用。
              【解决方案15】:

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

              我们可以通过使用multipledispatch来重载方法。

              【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多