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では、メソッドを「属性」の特別なセットと考えてください。オブジェクトに指定された名前の「属性」は1つ(つまり1つのメソッド)しか存在できません。最後のメソッドは、以前のメソッドを上書きします。Javaでは、メソッドはファーストクラスシチズン(「オブジェクトの属性」ではありません)ではなく、最も近い型(オーバーロードが発生する場所)に基づいて静的に解決される「送信メッセージ」によって呼び出されます。



6
この質問に対する答えがまだ受け入れられていないのはなぜですか?お気に入りの回答の左側にあるチェックマークをクリックしてください...
glglgl

回答:


150

メソッドのオーバーライドではなく、メソッドのオーバーロードです。そしてPythonでは、1つの関数ですべてを実行します。

class A:

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

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

Pythonで同じ名前の2つのメソッドを使用することはできません。その必要はありません。

Pythonチュートリアルの「デフォルトの引数値」セクションを参照してください。回避すべき一般的な間違いについては、「最小の驚き」と可変デフォルト引数を参照してください。

編集:Python 3.4の新しいシングルディスパッチの汎用関数については、PEP 443を参照してください。


119
そして、あなたはする必要はありません -私見時には、C ++のようにメソッドをオーバーロードすることは非常に便利です。わかりました。他の構成要素を使用して実行できないという意味では「必要」ではありませんが、いくつかのことをより簡単かつ単純にするでしょう。
Andreas Florath 2012

7
@AndreasFlorath同意しない。アヒルのタイピングが大好きで、各メソッドを記述して1つのことだけを行い、メソッドのオーバーロードの必要がないようにします。
agf 2012

4
巻き込まれる前に「回避すべき一般的な間違い」について読んでもらうための+1
mdup

43
少し同意しません;)...オーバーロードすると、多くの場合、コードがよりクリーンになります。これは、さまざまなケースを処理するためにif-elseステートメントが多すぎるメソッドをパックしないためです。ある意味で、関数型言語の全範囲は同様のアイデア、つまり引数パターンマッチングを使用します。つまり、判読不能な巨大なメソッドではなく、よりクリーンなメソッドを使用することになります。
2016

2
@ user1019129これが、Python 3.4で追加され、私の回答にリンクされている単一のディスパッチ汎用関数の目的です。
agf 2016

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
それが質問に対する唯一の有効な答えだと思います。できれば二重賛成です。
マイケル

3
それは良いですが、生の関数では機能せず、クラス内のメソッドだけで機能します。
合法スタック2019

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、変更可能なデフォルト値が必要な場合に役立ちます。個別の動作は個別の関数にある必要があります。
agf

@agf:None真のデフォルト値としても役立ちます。
Chris Morgan、

はい、しかし私はそれをセンチネル値として使用することを言及していました。それはあなたがあなたの答えでそれをどのように使用するかであり、私のコメントは明確だと思います。
agf

あなたは「一般的に」と言いますか?これが常にそうであるとは限らないことを意味していますか?
joel

24

あなたがすることはできません。

Pythonでは、すべてがオブジェクトです。クラスはものであるため、オブジェクトです。メソッドも同様です。

Aクラスと呼ばれるオブジェクトがあります。と呼ばれる属性がありますstackoverflow。そのような属性は1つだけ持つことができます。

と書くdef stackoverflow(...): ...と、メソッドであるオブジェクトを作成し、それをのstackoverflow属性に割り当てますA。2つの定義を記述する場合、2番目の定義が最初の定義を置き換えます。これは、割り当てが常に動作するのと同じ方法です。

さらに、オーバーロードが使用されることのある種類のワイルドなコードを記述したくありません。それは言語が機能する方法ではありません。

代わりに、(あなたがとにかく関数のパラメータ用のタイプを指定していないので、ほとんど意味がありません)あなたが与えられる可能性が事の種類ごとに別々の関数を定義しようとしているの停止は、物事が何を心配していると、彼らができるかを考え始めるんが

あなたはタプル対リストを処理するために別のものを書くことができないだけでなく、したくない、またはする必要がない

あなたがすることは、それらが両方とも、たとえば反復可能である(つまり、を書くことができるfor element in container:)ことを利用することです。(それらが継承によって直接関連していないという事実は無関係です。)


6
TBH、私は「必要がない」ともっと注意するでしょう。これは、現実世界のすべての機能にタグを付けたり、完全なプログラミング言語をチューリングしたりできるものであり、有効な引数ではありません。誰発電機を必要としていますか?誰クラスを必要としていますか?プログラミング言語は、より具体的なものへの単なる構文上の砂糖です。
セバスチャンマッハ

6
まったくそう思わない。「必要ない」または「したくない」かもしれませんが、切望したいアプリケーションは十分にあります。ハンドルPythonとのinstanceofのを使ってプログラムをポイ捨てせずに優雅にnumpyのアレイの両方...というプログラムを書くなどしてみてください
エルマー・ザンダー

1
マシの答えに基づいて、私は「あなたはできません」は今や不正確で時代遅れだと思います。@overloadデコレータの存在に基づいて、私は「本当にしたくない」というのは、せいぜい議論の余地があるだけだと思います。PEP-3124から、「...現在、受け取った引数のタイプを検査するPythonコードの一般的なアンチパターンです...これを行う「明白な方法」はタイプ検査ですが、これは脆弱で、拡張機能...」それで、Pythonの一部になったように、十分な人々が望んでいたようです。
マイクS

@MikeS、標準@overloadは入力のみです。
Narfanar

@Narfanar私のコメントにあなたの回答がどのように当てはまるのかわかりません。説明してもらえますか?
Mike 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
ところで:なぜこれが機能しないのか疑問に思う方のために、stackoverflow.com / questions / 52034771 /…のディスカッションを確認することをお勧めします。ed@overload関数には実際の実装はありません。これは、Pythonドキュメントの例からは明らかではありません。
マシ

15

私は自分の答えをPython 3.2.1で記述します。

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

使い方:

  1. overload 任意の量の呼び出し可能オブジェクトを受け取り、タプルに格納します functionsにから、lambdaを返します。
  2. ラムダは任意の量の引数を取りfunctions[number_of_unnamed_args_passed]、ラムダに渡された引数を使用して、calledに保存されている呼び出し関数の結果を返します。

使用法:

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'

引数を渡すと、最初の条件のロジックに従い、最初の印刷ステートメントが実行されます。引数を渡さない場合、else条件に入り、2番目の印刷ステートメントが実行されます。


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別の初期化子を渡した場合に1つの初期化子を実行したい場合を作成しようとすると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'
         ...

この概念は、必要に応じて異なる方法で複数の異なるシナリオに適用できます。



5

興味のある人のためにhttps://github.com/bintoro/overloading.pyに出くわしました。

リンクされたリポジトリのreadmeから:

オーバーロードは、ランタイム引数のタイプと数に基づいて関数のディスパッチを提供するモジュールです。

オーバーロードされた関数が呼び出されると、ディスパッチャは提供された引数を使用可能な関数シグネチャと比較し、最も正確に一致する実装を呼び出します。

特徴

登録時の機能検証と詳細な解決ルールにより、実行時に一意で明確な結果が保証されます。優れたパフォーマンスのために関数解決キャッシュを実装します。関数シグニチャーでオプションのパラメーター(デフォルト値)をサポートします。最適な一致を解決するときに、位置引数とキーワード引数の両方を評価します。フォールバック関数と共有コードの実行をサポートします。引数のポリモーフィズムをサポートします。クラスメソッドと静的メソッドを含むクラスと継承をサポートします。


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を提供する必要があります。

https://www.geeksforgeeks.org/python-method-overloading/からの礼儀


これは過負荷ではありません。これは上書きと呼ばれます。後者はPythonでサポートされています。1つ目は、デコレータを使用して実装できます。
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))

multipledispatchを使用してメソッドをオーバーロードできます


これには、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
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.