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 *