Sovraccarico delle funzioni in Python

1

Il mio libro dice che l'overloading delle funzioni non è possibile in Python ma mi fa domande come:

WAP to find out the volume of a cube,cuboid and cylinder using function overloading.

Devo coinvolgere i decoratori qui?

Come dovrei comportare un sovraccarico funzionale nel mio programma?

Ho visto una domanda simile in cui è stato utilizzato il wrapping da functools ma non è riuscito a capirlo.

Se l'overloading funzionale non è coinvolto, allora è un gioco da ragazzi ...

P.S-Sono al grado 12 e non ci è stato insegnato molto sui decoratori.

    
posta Jai Mahajan 07.12.2015 - 12:59
fonte

3 risposte

4

Se stai usando Python 3.4 o più recente, hai accesso a functools.singledispatch (grazie a PEP 443 ), che ti consente di sovraccaricare le funzioni (preferibilmente unario) come te sarebbe in una lingua come Java.

Se definisci i tuoi vari poliedri come classi, puoi inviare i loro tipi, in questo modo:

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)

Ora puoi fare questo:

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

Se stai utilizzando una versione precedente alla 3.4, non hai davvero delle buone opzioni per questo tipo di singolo invio trasparente (anche se potresti sempre backport functools.singledispatch se sei su un precedente 3.x - è scritto in Python puro e non credo che faccia affidamento su nessuna nuova funzionalità 3.4). Potresti, invece, avere una funzione volume(cube=None, cuboid=None, cylinder=None) , o volume(polyhedron) e invio su type(polyhedron) all'interno della funzione, o fare un po 'di magia con **kwargs .

Tutto ciò che ho detto, in qualche modo dubito che il tuo libro voglia usare functools.singledispatch - rischierei di indovinare che qualcuno ha appena copiato i problemi da un C ++ o da un libro Java senza pensarci davvero.

    
risposta data 07.12.2015 - 17:46
fonte
1

È possibile che il libro stia usando una formulazione errata e in effetti ti sta chiedendo di usare l'ereditarietà 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():
        ...
    
risposta data 07.12.2015 - 18:38
fonte
1

Python non supporta l'overloading delle funzioni, tuttavia ti dà gli strumenti per implementarlo tu stesso in un modo abbastanza semplice.

Usando **kw nell'elenco dei parametri passeranno tutti gli argomenti in un dizionario. Per simulare l'overloading delle funzioni, testate personalmente l'esistenza dei parametri e inviate in modo appropriato:

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)

Questo richiede più legwork, ma è più flessibile dell'overloading. Rende più facile "sovraccaricare" in modi che coinvolgono un'implementazione single core, ad esempio:

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)
    
risposta data 07.12.2015 - 23:14
fonte

Leggi altre domande sui tag