Sobrecargar funciones y operadores en Python

    驴Qu茅 es la sobrecarga?

    La sobrecarga, en el contexto de la programaci贸n, se refiere a la capacidad de una funci贸n o de un operador de comportarse de diferentes maneras dependiendo de los par谩metros que se pasan a la funci贸n o de los operandos sobre los que act煤a el operador. En este art铆culo, veremos c贸mo podemos realizar la sobrecarga de funciones y la sobrecarga de operadores en Python.

    Sobrecargar un m茅todo fomenta la reutilizaci贸n. Por ejemplo, en lugar de escribir varios m茅todos que difieren solo ligeramente, podemos escribir un m茅todo y sobrecargarlo. La sobrecarga tambi茅n mejora la claridad del c贸digo y elimina la complejidad.

    La sobrecarga es un concepto muy 煤til. Sin embargo, tiene una serie de desventajas asociadas. La sobrecarga puede causar confusi贸n cuando se usa a trav茅s de los l铆mites de herencia. Cuando se usa en exceso, se vuelve engorroso administrar funciones sobrecargadas.

    En la secci贸n restante de este art铆culo, discutiremos en detalle la funci贸n y la sobrecarga del operador.

    Sobrecarga de funciones en Python

    Dependiendo de c贸mo se haya definido la funci贸n, podemos llamarla con cero, uno, dos o incluso muchos par谩metros. Esto se conoce como “sobrecarga de funciones”.

    La sobrecarga de funciones se divide en dos tipos: sobrecarga de funciones integradas y sobrecarga de funciones personalizadas. Veremos ambos tipos en las pr贸ximas secciones.

    Sobrecarga de funciones integradas

    Es posible que cambiemos el comportamiento predeterminado de las funciones integradas de Python. Solo tenemos que definir el m茅todo especial correspondiente en nuestra clase.

    Demostremos esto usando Python len() funci贸n en nuestra clase de compra:

    class Purchase:
        def __init__(self, basket, buyer):
            self.basket = list(basket)
            self.buyer = buyer
    
        def __len__(self):
            return len(self.basket)
    
    purchase = Purchase(['pen', 'book', 'pencil'], 'Python')
    print(len(purchase))
    

    Salida:

    3
    

    Para cambiar la forma en que len() funci贸n se comporta, definimos un m茅todo especial llamado _len_() en nuestra clase. Cada vez que pasamos un objeto de nuestra clase a len(), el resultado se obtendr谩 llamando a nuestra funci贸n definida personalizada, es decir, _len_().

    La salida muestra que podemos usar len() para obtener la longitud de la canasta.

    Si llamamos len() en el objeto sin el __len__() funci贸n sobrecargada, obtendremos un TypeError como se muestra a continuaci贸n:

    class Purchase:
        def __init__(self, basket, buyer):
            self.basket = list(basket)
            self.buyer = buyer
    
    purchase = Purchase(['pen', 'book', 'pencil'], 'Python')
    print(len(purchase))
    

    Salida:

    Traceback (most recent call last):
      File "C:/Users/admin/func.py", line 8, in <module>
        print(len(purchase))
    TypeError: object of type 'Purchase' has no len()
    

    Nota: Python espera el len() funci贸n para devolver un n煤mero entero, por lo tanto, esto debe tenerse en cuenta al sobrecargar la funci贸n. Si se espera que su funci贸n sobrecargada devuelva cualquier otra cosa que no sea un n煤mero entero, obtendr谩 un TypeError.

    Podemos cambiar el comportamiento del len() m茅todo en el ejemplo anterior desde dentro de la definici贸n de su implementaci贸n, es decir, __len__(). En lugar de devolver la longitud de la canasta, hagamos que devuelva algo m谩s:

    class Purchase:
        def __init__(self, basket, buyer):
            self.basket = list(basket)
            self.buyer = buyer
    
        def __len__(self):
            return 10;
    
    purchase = Purchase(['pen', 'book', 'pencil'], 'Python')
    print(len(purchase))
    

    Salida:

    10
    

    En lugar de devolver la longitud de la cesta, ahora devuelve el valor que hemos especificado.

    Sobrecarga de funciones definidas por el usuario

    Para sobrecargar una funci贸n definida por el usuario en Python, necesitamos escribir la l贸gica de la funci贸n de tal manera que, dependiendo de los par谩metros pasados, se ejecute un fragmento de c贸digo diferente dentro de la funci贸n. Eche un vistazo al siguiente ejemplo:

    class Student:
        def hello(self, name=None):
            if name is not None:
                print('Hey ' + name)
            else:
                print('Hey ')
    
    # Creating a class instance
    std = Student()
    
    # Call the method
    std.hello()
    
    # Call the method and pass a parameter
    std.hello('Nicholas')
    

    Salida:

    Hey
    Hey Nicholas
    

    Hemos creado la clase Student con una funci贸n llamada hello(). La clase toma el par谩metro name que se ha establecido en None. Esto significa que se puede llamar al m茅todo con o sin un par谩metro.

    Hemos creado una instancia de la clase que se ha utilizado para llamar a la funci贸n dos veces, primero con cero par谩metros y en segundo lugar con un par谩metro. Hemos implementado la sobrecarga de funciones ya que hay dos formas de llamar a la funci贸n.

    Ahora que sabemos c贸mo funciona la sobrecarga de funciones, la siguiente secci贸n se centra en la sobrecarga del operador.

    Sobrecarga del operador

    Python nos permite cambiar el comportamiento predeterminado de un operador seg煤n los operandos que usemos. Esta pr谩ctica se conoce como “sobrecarga del operador”.

    La funcionalidad de los operadores de Python depende de las clases integradas. Sin embargo, el mismo operador se comportar谩 de manera diferente cuando se aplique a diferentes tipos. Un buen ejemplo es el operador “+”. Este operador realizar谩 una operaci贸n aritm茅tica cuando se aplique a dos n煤meros, concatenar谩 dos cadenas y fusionar谩 dos listas.

    Ejemplos de sobrecarga del operador

    Para ver la sobrecarga del operador de Python en acci贸n, inicie la terminal de Python y ejecute los siguientes comandos:

    >>> 4 + 4
    8
    >>> "Py" + "thon"
    'Python'
    

    En el primer comando, usamos el operador “+” para sumar dos n煤meros. En el segundo comando, usamos el mismo operador para concatenar dos cadenas.

    En este caso, el operador “+” tiene dos interpretaciones. Cuando se utiliza para sumar n煤meros, se denomina “operador de suma”. Cuando se utiliza para agregar cadenas, se denomina “operador de concatenaci贸n”. En resumen, podemos decir que el operador “+” se ha sobrecargado para int y str clases.

    Para lograr la sobrecarga del operador, definimos un m茅todo especial en una definici贸n de clase. El nombre del m茅todo debe comenzar y terminar con un subrayado doble (__). El operador + est谩 sobrecargado usando un m茅todo especial llamado __add__(). Este m茅todo es implementado tanto por el int y str clases.

    Considere la siguiente expresi贸n:

    x + y
    

    Python interpretar谩 la expresi贸n como x.__add__(y). La versi贸n de __add__() que se llame depender谩 de los tipos de x y y. Por ejemplo:

    >>> x, y = 5, 7
    
    >>> x + y
    
    12
    >>> x.__add__(y)
    12
    >>>
    

    El ejemplo anterior demuestra c贸mo utilizar el operador + as铆 como su m茅todo especial.

    El siguiente ejemplo demuestra c贸mo sobrecargar varios operadores en Python:

    import math
    
    class Point:
    
        def __init__(self, xCoord=0, yCoord=0):
            self.__xCoord = xCoord
            self.__yCoord = yCoord
    
        # get x coordinate
        def get_xCoord(self):
            return self.__xCoord
    
        # set x coordinate
        def set_xCoord(self, xCoord):
            self.__xCoord = xCoord
    
        # get y coordinate
        def get_yCoord(self):
            return self.__yCoord
    
        # set y coordinate
        def set_yCoord(self, yCoord):
            self.__yCoord = yCoord
    
        # get current position
        def get_position(self):
            return self.__xCoord, self.__yCoord
    
        # change x & y coordinates by p & q
        def move(self, p, q):
            self.__xCoord += p
            self.__yCoord += q
    
        # overload + operator
        def __add__(self, point_ov):
            return Point(self.__xCoord + point_ov.__xCoord, self.__yCoord + point_ov.__yCoord)
    
        # overload - operator
        def __sub__(self, point_ov):
            return Point(self.__xCoord - point_ov.__xCoord, self.__yCoord - point_ov.__yCoord)
    
        # overload < (less than) operator
        def __lt__(self, point_ov):
            return math.sqrt(self.__xCoord ** 2 + self.__yCoord ** 2) < math.sqrt(point_ov.__xCoord ** 2 + point_ov.__yCoord ** 2)
    
        # overload > (greater than) operator
        def __gt__(self, point_ov):
            return math.sqrt(self.__xCoord ** 2 + self.__yCoord ** 2) > math.sqrt(point_ov.__xCoord ** 2 + point_ov.__yCoord ** 2)
    
        # overload <= (less than or equal to) operator
        def __le__(self, point_ov):
            return math.sqrt(self.__xCoord ** 2 + self.__yCoord ** 2) <= math.sqrt(point_ov.__xCoord ** 2 + point_ov.__yCoord ** 2)
    
        # overload >= (greater than or equal to) operator
        def __ge__(self, point_ov):
            return math.sqrt(self.__xCoord ** 2 + self.__yCoord ** 2) >= math.sqrt(point_ov.__xCoord ** 2 + point_ov.__yCoord ** 2)
    
        # overload == (equal to) operator
        def __eq__(self, point_ov):
            return math.sqrt(self.__xCoord ** 2 + self.__yCoord ** 2) == math.sqrt(point_ov.__xCoord ** 2 + point_ov.__yCoord ** 2)
    
    point1 = Point(2, 4)
    point2 = Point(12, 8)
    
    print("point1 < point2:", point1 < point2)
    print("point1 > point2:", point1 > point2)
    print("point1 <= point2:", point1 <= point2)
    print("point1 >= point2:", point1 >= point2)
    print("point1 == point2:", point1 == point2)
    

    Salida:

    point1 < point2: True
    point1 > point2: False
    point1 <= point2: True
    point1 >= point2: False
    point1 == point2: False
    

    Tenemos dos atributos privados en la clase Point, a saber, __xCoord y __yCoord que representa las coordenadas cartesianas del plano xCoord y yCoord. Hemos definido los m茅todos setter y getter para estos atributos. los get_position() El m茅todo nos ayuda a obtener la posici贸n actual mientras el move() El m茅todo nos ayuda a cambiar las coordenadas.

    Considere la siguiente l铆nea extra铆da del c贸digo:

        def __add__(self, point_ov):
    

    La l铆nea nos ayuda a sobrecargar el operador + para nuestra clase. los __add__() El m茅todo debe crear un nuevo objeto Point agregando las coordenadas individuales de un solo objeto Point a otro objeto Point. Finalmente devuelve el objeto reci茅n creado a la persona que llama. Esto nos ayuda a escribir expresiones como:

    point3 = point1 + point2
    

    Python interpretar谩 lo anterior como point3 = point1.__add__(point2). Entonces llamar谩 al __add__() m茅todo para agregar dos objetos Point. El resultado se asignar谩 al “punto3”.

    Tenga en cuenta que una vez __add__() se llama al m茅todo, el valor de point1 ser谩 asignado a self par谩metro mientras que el valor de point2 ser谩 asignado a point_ovpar谩metro. Todos los dem谩s m茅todos especiales funcionar谩n de manera similar.

    Operadores a sobrecargar

    La siguiente tabla muestra algunos de los operadores matem谩ticos sobrecargados m谩s com煤nmente y el m茅todo de clase para sobrecargar:

    M茅todo del operador

    +__add__(self, other)
    -__sub__(self, other)
    *__mul__(self, other)
    /__truediv__(self, other)
    %__mod__(self, other)
    __lt__(self, other)
    =__le__(self, other)
    ==__eq__(self, other)
    !=__ne__(self, other)
    >__gt__(self, other)
    >=__ge__(self, other)

    Conclusi贸n

    Python admite la sobrecarga de funciones y operadores. En la sobrecarga de funciones, podemos usar el mismo nombre para muchas funciones de Python pero con diferentes n煤meros o tipos de par谩metros. Con la sobrecarga de operadores, podemos cambiar el significado de un operador de Python dentro del alcance de una clase.

     

    Etiquetas:

    Deja una respuesta

    Tu direcci贸n de correo electr贸nico no ser谩 publicada. Los campos obligatorios est谩n marcados con *