web-dev-qa-db-ja.com

Pythonでの関数のオーバーロード

私の本では、pythonでは関数のオーバーロードは不可能であると述べていますが、次のような質問をします:

WAPは、関数のオーバーロードを使用して、立方体、立方体、および円柱の体積を検出します。

ここにデコレータを含める必要がありますか?

プログラムに関数のオーバーロードを含めるにはどうすればよいですか?

Functoolsからのラップが使用された同様の質問を見ましたが、理解できませんでした。

機能的なオーバーロードが関与していない場合、それは子供の遊びです...

P.S-私は12年生で、デコレーターについてはあまり教えられていません。

1
Jai Mahajan

Python 3.4以降を使用している場合、 _functools.singledispatch_ にアクセスできます(- PEP 44 に感謝)。 Javaのような言語と同じように、関数(できれば単項)をオーバーロードできます。

さまざまな多面体をクラスとして定義すると、次のように、それらのタイプにディスパッチできます。

_import math
from functools import singledispatch


class Cube:
    def __init__(self, x):
        self.x = x

class Cuboid:
    def __init__(self, x, y, z):
        self.x = x
        self.y = y
        self.z = z

class Cylinder:
    def __init__(self, *, r, h):
        self.r = r
        self.h = h

# Mark this function as single-dispatchable.
@singledispatch
def volume(polyhedron):
    pass # Or throw an exception, or something like that.

def _volume_cube(cube):
    return cube.x ** 3

def _volume_cuboid(cuboid):
    return cuboid.x * cuboid.y * cuboid.z

def _volume_cylinder(cylinder):
    return math.pi * (cylinder.r) ** 2 * cylinder.h

# Register handlers for the types for which you can compute volumes.
volume.register(Cube, _volume_cube)
volume.register(Cuboid, _volume_cuboid)
volume.register(Cylinder, _volume_cylinder)
_

今、これを行うことができます:

_>>> cube = Cube(4)
>>> volume(cube)
64
>>> cuboid = Cuboid(3, 5, 7)
>>> volume(cuboid)
105
>>> cylinder = Cylinder(r=2, h=4)
>>> volume(cylinder)
50.26548245743669
_

3.4よりも古いバージョンを使用している場合、この種の透過的な単一ディスパッチには本当に良いオプションがありません(ただし、以前の3.xを使用している場合は、常に_functools.singledispatch_をバックポートすることもできます)。純粋なPythonで記述されており、3.4の新機能に依存しているとは思いません)代わりに、関数volume(cube=None, cuboid=None, cylinder=None)またはvolume(polyhedron)および関数内のtype(polyhedron)でディスパッチするか、_**kwargs_で魔法をかけます。

とはいえ、あなたの本が_functools.singledispatch_を使用することを望んでいることはどういうわけか疑っています-誰かがC++またはJava本なしで本から問題をコピーしただけだと思う​​ので危険ですそれについて考えます。

4
senshin

本が不適切な表現を使用していて、実際にOO継承を使用するように求めている可能性があります。

class Shape(object):
    def volume():
        raise NotImplementedError()

class Cube(Shape):
    def __init__(self, side_length):
        self.side = side_length
    def volume():
        return self.side * self.side * self.side

class Cylinder(Shape):
    def volume():
        ...
1
Daenyth

Pythonは関数のオーバーロードをサポートしていませんが、かなり簡単な方法で自分で実装するためのツールを提供しています。

パラメータリストで**kwを使用すると、すべての引数が辞書で渡されます。関数のオーバーロードをシミュレートするには、パラメーターの存在を自分でテストし、適切にディスパッチします。

def foo_x_y(x, y):
    print("x is %d y is %d" % (x,y))

def foo_h_l(h, l):
    print("h is %d l is %d" % (h,l))

def foo(**kw):
    if 'x' in kw and 'y' in kw:
        return foo_x_y(kw['x'], kw['y'])
    Elif 'h' in kw and 'l' in kw:
        return foo_h_l(kw['h'], kw['l'])

    raise TypeError("foo() takes exactly 2 argument (%d given)" % len(kw))


>> foo(x=1, y=2)
x is 1 y is 2
>> foo(h=2, l=1)
h is 2 l is 1
>> foo(z=1)
File "foo.py", line 18, in <module>
   foo(z=1)
File "foo.py", line 13, in foo
   raise TypeError("foo() takes exactly 2 argument (%d given)" % len(kw))
TypeError: foo() takes exactly 2 argument (1 given)

これはより多くのレッグワークを必要としますが、オーバーロードよりも柔軟性があります。これにより、次のように、単一のコア実装を伴う方法で「オーバーロード」が容易になります。

def arc_length(**kw):
    if 'diameter' not in kw:
        raise TypeError

    if 'radians' not in kw or 'degrees' not in kw:
        raise TypeError

    if 'radians' in kw and 'degrees' in kw:
        raise TypeError

    diameter = kw['diameter']

    if degrees in kw:
        radians = degrees_to_radians(kw['degrees'])
    else:
        radians = kw['radians'] 

    # calculate arc length using radians and diameter

 arc_length(diameter=10, degrees=30)
 arc_length(diameter=10, radians=1.2)
1
Gort the Robot