為什麼 Python 不支持函數重載?其他函數大部分都支持的?


這個問題,最近在 cpyug 上面討論得很火熱。我簡要概括一下。

為了考慮為什麼 python 不提供函數重載,首先我們要研究為什麼需要提供函數重載。

函數重載主要是為了解決兩個問題。

1。可變參數類型。

2。可變參數個數。

另外,一個基本的設計原則是,僅僅當兩個函數除了參數類型和參數個數不同以外,其功能是完全相同的,此時才使用函數重載,如果兩個函數的功能其實不同,那麼不應當使用重載,而應當使用一個名字不同的函數。

好吧,那麼對於情況 1 ,函數功能相同,但是參數類型不同,python 如何處理?答案是根本不需要處理,因為 python 可以接受任何類型的參數,如果函數的功能相同,那麼不同的參數類型在 python 中很可能是相同的代碼,沒有必要做成兩個不同函數。

那麼對於情況 2 ,函數功能相同,但參數個數不同,python 如何處理?大家知道,答案就是預設參數。對那些缺少的參數設定為預設參數即可解決問題。因為你假設函數功能相同,那麼那些缺少的參數終歸是需要用的。

好了,鑒於情況 1 跟 情況 2 都有了解決方案,python 自然就不需要函數重載了。


我補充一點,Python3是可以通過metaclass + parameter annotation使某個類假裝支持function overloading

class Spam(metaclass=MultipleMeta):
def bar(self, x:int, y:int):
print("Bar 1:", x, y)
def bar(self, s:str, n:int = 0):
print("Bar 2:", s, n)

# Example: overloaded __init__
import time
class Date(metaclass=MultipleMeta):
def __init__(self, year:int, month:int, day:int):
self.year = year
self.month = month
self.day = day

def __init__(self):
t = time.localtime()
self.__init__(t.tm_year, t.tm_mon, t.tm_mday)

其中MultipleMeta的實現可以參考http://chimera.labs.oreilly.com/books/1230000000393/ch09.html#_discussion_162,簡單來說就是實現一個attribute與functions的一對多關係。

@pansz 對於function overloading的總結已經很好了。就我個人理解,functino overloading無非就是想要解決取名難的問題(代碼耦合的問題可以通過重構解決),但事實上這也不算是什麼難題,而且Python的parameter設定也足以應付絕大多數需求了,所以社區基於效率與語言複雜度的考慮,乾脆就不支持function overloading了。當然以上都是我瞎猜的。


誰說不支持了?自己實現個簡易的 multiple dispatch 不就完了。Guido 七年前就給了範例了 [1]。同學們要多讀書,不要隨便被問題挖的坑給埋了……

靜態語言需要 function overloading 主要是為了解決調用靈活性的問題,Python 這麼靈活的腳本語言用 function overloading 純屬多此一舉。

[1]: http://www.artima.com/weblogs/viewpost.jsp?thread=101605


首選要明白支持函數重載的目的是什麼?

在靜態語言中,方法重載是希望類可以以統一的方式處理不同類型的數據提供了可能。多個同名函數同時存在,具有不同的參數個數/類型,重載是一個類中多態性的一種表現。

在Java中實現函數重載:

class Writer{
public static void write(StringIO output, String content){
output.write(content);
return null;
}

public static void write(File output, String content){
output.write(content);
return null;
}
}

而在動態語言中,有鴨子類型,如果走起路來像鴨子,叫起來也像鴨子,那麼它就是鴨子。一個對象的特徵不是由它的類型決定,而是通過對象中的方法決定,所以函數重載在動態語言中就顯得沒有意義了,因為函數可以通過鴨子類型來處理不同類型的對象,鴨子類型也是多態性的一種表現。

在Python中實現函數重載:

clsss Writer:
@staticmethod
def write(output, content):
# output對象只要實現了write方法就行
output.write(content)

# stringIO類型
output = StringIO.StringIO()
Writer.write(output, "helloworld")

# file 類型
output = open("out.txt", "w")
Writer.write(output, "helloworld")


還有別的語言支持這麼變態的特性嗎?請問:

cos(float) cos(double) 和 cosf(float) cosd(double) 兩者相比,前者的易用性在哪?能讓代碼更簡潔?


[del]#像這種要怎麼搞啊?[/del]

這個例子舉得不太恰當,因為python可以用類的 self.__add__ 實現

我是想說,如果是一種特殊的操作(不是add這麼簡單的,那麼要如何實現引用類內的值呢)

class A:
value = 1

def add(x, y):
pass
#如何判斷返回 x+y 還是 x.value+y.value ?

a1 = A()
a2 = A()
add(1,1)
add(a1,a2)


Python支持可選參數,比如

def func( a, b = 0, c = 0 ):

pass

那麼,形如func(1),func(1,2),func(1,2,3)或者func(a=1, b=2)這樣的調用都是合法的,這種情況下函數重載就顯得雞肋了。

個人以為,一個語言選擇重載或者可選參數兩者中的一種就是了。像C# 4這樣同時支持這兩種形式的,就比較容易混淆。


你錯了, 重載overload不是多態的overwrite。 重載是指用相同的函數名,但是參數列表不同(類型和個數), 是不需要虛函數也不需要額外開銷的,是在編譯期由編譯器重命名函數。如 int f(int) , int f(float); 編譯其可能函數名變為int f_i_1(int) 和 int f_f_1(float). 腳本語言不需要重載機制是入參本身的類型就不是確定的,既然類型可以是任意 也就帶不來性能上的提高, 就無意義了。 比如python的 sub f(a): , 任意的類型都可以用來刁用f


其實是支持的,反對樓上各種說重載沒有用的。對於 Python 這種動態語言來說(參數類型和參數個數),重載和重寫可以看做一個東西。

我們利用重載函數這種機制的目的是用 同一個函數,來處理不同類型或者不同個數的參數。

注意重點,同一個函數,換一句話說就是相同的函數名字。

在不使用重載的情況下,處理不同參數只能靠 if-else,這種寫法明顯很不 pythonic。

python 中 singledispatch 支持函數重載,更準確的來說是單泛函數,根據第一個參數類型來決定使用的是哪個函數。

函數:

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

更多的請看文檔鏈接。

實現過程如下:

  1. 生成單泛函數
  2. 註冊函數
  3. 修復 MRO
  4. 調用註冊函數


phython是dynamic binding, 就是說已經可以隨便重載,不需要專門定義重載


動態語言,無需重載

Python will Auto Reload


最近新學了一招,驚為天人。

用classmethod實現類似構造函數重載。

class Overloading(object):
def __init__(self, x):
self.x = x

@classmethod
def NewInit(cls, x, y):
b = cls(x*y)
b.z = x - y
return b

a = Overloading(4)
b = Overloading.NewInit(4,5)

說實話我從來沒有想到classmethod還有這用法。


@functools.singledispatch(default)

Transforms a function into a single-dispatch generic function.


python 用得著重載嗎?


def max(**kwargs) 可用。


參數類型不確定的話使用def foo(*arg)


為啥不行?

def max(a,b,c="nothing"):

if c=="nothing":

if a &> b:

print a

else:

print b

else:

......

這比重載更複雜嗎?


pansz 總結的看似有理,細想還是有問題。舉個例子說明,

假設一個函數求2個給定參數的最大值 max1(a,b), 另一個求給定3個參數求最大值 max2(a,b,c)

如c++重載實現可以是

int max(int a, int b); int max(int a, int b, int c);

用python如何做到呢?

按你們的討論 可能是定義 def max(a,b,c=0) 就可以了 其實是不行的你求不出2個中的最大值,

不行看看 max(-1,-2)的結果是啥?

問題的關鍵不是函數的輸入參數有多靈活和強大 而是函數的功能實現是建立在對參數的了解上。

所以python為啥不支持重載?沒有答案


推薦閱讀:

請問要開發安卓,需要考什麼證?
精通C++之後是否寫代碼就是體力活了?
PyPy 為什麼會比 CPython 還要快?
為什麼 C 語言被設計成函數需要先聲明才能被使用?
C# 或者 SQL Server 生成的 GUID 有沒有可能重複?

TAG:編程語言 | Python |