我试图在Python中实现方法重载:
class A:
def stackoverflow(self):
print 'first method'
def stackoverflow(self, i):
print 'second method', i
ob=A()
ob.stackoverflow(2)
但是输出是第二种方法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中,你可以在一个函数中完成所有工作:
class A:
def stackoverflow(self, i='some_default_value'):
print('only method')
ob=A()
ob.stackoverflow(2)
ob.stackoverflow()
请参阅Python教程的默认实参值部分。请参阅“Least surprise”和可变默认参数,以了解需要避免的常见错误。
有关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)
在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
你不能,永远不需要,也不想这么做。
在Python中,所有东西都是对象。类是事物,所以它们是对象。方法也是如此。
有一个对象叫做A,它是一个类。它有一个名为stackoverflow的属性。它只能有一个这样的属性。
当你写def stackoverflow(…):…,所发生的是你创建一个对象,它是方法,并将它赋值给a的stackoverflow属性。如果你写了两个定义,第二个会替换第一个,这与赋值的行为相同。
此外,您也不希望编写的代码执行重载有时会用到的那种过分的操作。语言不是这样的。
与其尝试为可能给定的每种类型的东西定义一个单独的函数(这没什么意义,因为你没有为函数参数指定类型),不如停止担心这些东西是什么,开始思考它们能做什么。
您不仅不能单独编写一个来处理元组和列表,而且也不想或不需要这样做。
你所要做的就是利用它们都是可迭代的这一事实,例如,你可以写for element in container:)。(他们之间没有直接的遗传关系,这一点无关紧要。)
我用Python 3.2.1写了我的答案。
def overload(*functions):
return lambda *args, **kwargs: functions[len(args)](*args, **kwargs)
工作原理:
Overload接受任意数量的可调用对象,并将它们存储在元组函数中,然后返回lambda。
接受任意数量的参数,
然后返回存储在函数[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) \
)
我用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
在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'
...
这个概念可以根据需要通过不同的方法应用到多个不同的场景中。
在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重载该方法。
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,以便在调用时提供不同数量的参数。
方法重载
在3.4之前,agf的答案是正确的,而现在有了PEP-3124,我们得到了语法糖。
有关@overload装饰器的详细信息,请参阅typing文档,但请注意,这真的只是语法糖,恕我冒犯,这是人们一直在争论的问题。
就我个人而言,我同意拥有多个具有不同签名的函数比拥有一个具有20多个参数都设置为默认值(大多数时候没有)的单一函数更具可读性,然后不得不使用无休止的if, elif, else链来找出调用者实际希望我们的函数对所提供的参数集做什么。在Python禅之后,这是早就该有的:
美总比丑好。
也可以说
简单比复杂好。
直接来自上面链接的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文档中的示例中并不明显。
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)
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
Python 3.5增加了类型模块。这包括一个重载装饰器。
这个装饰器的目的是帮助类型检查器。功能上它只是鸭子打字。
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中的以下类型信息:
虽然这可能有所帮助,但请注意,这添加了许多“奇怪的”新语法。它的目的——纯粹的类型提示——不是很明显。
使用类型的联合通常是更好的选择。