Por lo que recuerdo de mi clase de C ++, el profesor dijo que la sobrecarga del operador es genial, pero dado que requiere relativamente mucho pensamiento y código para cubrir todos los casos finales (por ejemplo, cuando se sobrecarga + probablemente también desee sobrecargar ++ y +=, y también asegúrese de manejar casos finales como agregar un objeto a sí mismo, etc.), solo debe considerarlo en aquellos casos en los que esta función tendrá un impacto importante en su código, como sobrecargar los operadores para la clase de matriz en una aplicación matemática.

¿Se aplica lo mismo a Python? ¿Recomendaría anular el comportamiento del operador en python? ¿Y qué reglas generales me puedes dar?

13
olamundo 12 oct. 2009 a las 05:00

4 respuestas

La mejor respuesta

La sobrecarga del operador es principalmente útil cuando crea una nueva clase que se incluye en una "Clase base abstracta" (ABC) existente; de hecho, muchos de los ABC en el módulo de biblioteca estándar collections dependen de la presencia de ciertos métodos especiales (y métodos especiales, uno con nombres que comienzan y terminan con guiones bajos, también conocidos como" dunders ") , son exactamente la forma en que realiza la sobrecarga del operador en Python). Esto proporciona una buena orientación inicial.

Por ejemplo, una clase Container debe anular el método especial __contains__, es decir, el operador de verificación de membresía item in container (como en if item in container: - don No confunda con la declaración for, for item in container:, que se basa en __iter__! -). Del mismo modo, un Hashable debe anular __hash__, un Sized debe anular __len__, un Sequence o un Mapping deben anular __getitem__ , Etcétera. (Además, el ABC puede proporcionar a su clase una funcionalidad mixin; por ejemplo, tanto Sequence como Mapping pueden proporcionar __contains__ en función de su anulación __getitem__ suministrada y, por lo tanto, convierte automáticamente tu clase en Container).

Más allá de collections, deseará anular métodos especiales (es decir, prever la sobrecarga del operador) principalmente si su nueva clase "es un número". Existen otros casos especiales, pero resisten la tentación de sobrecargar a los operadores "solo por frialdad", sin conexión semántica a los significados "normales", como lo hacen las transmisiones de C ++ para << y >> y cadenas de Python (en Python 2.*, afortunadamente ya no está en 3.* ;-) hazlo por % - cuando dichos operadores ya no significan "desplazamiento de bits" o "resto de división", tú ' solo está engendrando confusión. La biblioteca estándar de un idioma puede salirse con la suya (aunque no debería ;-), pero a menos que su biblioteca se generalice tanto como la estándar del idioma, ¡la confusión dolerá! -)

24
Alex Martelli 12 oct. 2009 a las 01:23

La sobrecarga de Python es "más segura" en general que la de C ++; por ejemplo, el operador de asignación no se puede sobrecargar y += tiene una implementación predeterminada sensata.

Sin embargo, en algunos aspectos, la sobrecarga en Python sigue siendo tan "rota" como en C ++. Los programadores deben restringir el deseo de "reutilizar" un operador para fines no relacionados, como C ++ reutilizando los cambios de bits para realizar el formateo y el análisis de cadenas. No sobrecargue un operador con una semántica diferente de su implementación solo para obtener una sintaxis más bonita.

El estilo moderno de Python desalienta fuertemente la sobrecarga "deshonesta", pero muchos aspectos del lenguaje y la biblioteca estándar retienen operadores mal nombrados para la compatibilidad con versiones anteriores. Por ejemplo:

  • %: módulo y formato de cadena
  • +: suma y secuencia de concatenación
  • *: multiplicación y repetición de secuencia

Entonces, ¿regla general? Si la implementación de su operador sorprenderá a las personas, no lo haga.

3
John Millikin 12 oct. 2009 a las 01:15

Aquí hay un ejemplo que usa la operación bit a bit u simulación de una tubería de Unix. Esto pretende ser un ejemplo contrario a la mayoría de las reglas generales.

Acabo de encontrar Lumberjack que usa esta sintaxis en código real



class pipely(object):
    def __init__(self, *args, **kw):
        self._args = args
        self.__dict__.update(kw)

    def __ror__(self, other):
        return ( self.map(x) for x in other if self.filter(x) )

    def map(self, x):
        return x

    def filter(self, x):
        return True

class sieve(pipely):
    def filter(self, x):
        n = self._args[0]
        return x==n or x%n

class strify(pipely):
    def map(self, x):
        return str(x)

class startswith(pipely):
    def filter(self, x):
        n=str(self._args[0])
        if x.startswith(n):
            return x

print"*"*80
for i in xrange(2,100) | sieve(2) | sieve(3) | sieve(5) | sieve(7) | strify() | startswith(5):
    print i

print"*"*80
for i in xrange(2,100) | sieve(2) | sieve(3) | sieve(5) | sieve(7) | pipely(map=str) | startswith(5):
    print i

print"*"*80
for i in xrange(2,100) | sieve(2) | sieve(3) | sieve(5) | sieve(7) | pipely(map=str) | pipely(filter=lambda x: x.startswith('5')):
    print i

6
John La Rooy 12 oct. 2009 a las 01:56

He escrito software con cantidades significativas de sobrecarga, y últimamente lamento esa política. Yo diría esto:

Solo sobrecargue a los operadores si es lo natural que se espera hacer y no tiene ningún efecto secundario.

Entonces, si crea una nueva clase RomanNumeral, tiene sentido sobrecargar la suma y la resta, etc. Pero no la sobrecargue a menos que sea natural: no tiene sentido definir la suma y la resta para un Car o un objeto Vehicle.

Otra regla general: no sobrecargue == . Hace que sea muy difícil (aunque no imposible) probar si dos objetos son iguales. Cometí este error y lo pagué durante mucho tiempo.

En cuanto a cuándo sobrecargar +=, ++, etc., en realidad diría: solo sobrecargue operadores adicionales si tiene mucha demanda para esa funcionalidad. Es más fácil tener una forma de hacer algo que cinco. Claro, significa que a veces tendrá que escribir x = x + 1 en lugar de x += 1, pero más código está bien si es más claro.

En general, al igual que con muchas características 'sofisticadas', es fácil pensar que quieres algo cuando realmente no lo haces, implementar un montón de cosas, no notar los efectos secundarios y luego resolverlo más tarde. Errar en el lado conservador.

EDITAR: quería agregar una nota explicativa sobre la sobrecarga ==, porque parece que varios comentaristas malinterpretan esto, y me ha sorprendido. Sí, is existe, pero es una operación diferente. Digamos que tengo un objeto x, que es de mi clase personalizada o es un número entero. Quiero ver si x es el número 500. Pero si configura x = 500, luego pruebe x is 500, obtendrá False, debido a la forma en que Python almacena en caché los números . Con 50, devolvería True. Pero no puede usar is, porque es posible que desee que x == 500 devuelva True si x es una instancia de su clase. ¿Confuso? Seguro. Pero este es el tipo de detalle que necesita comprender para sobrecargar con éxito a los operadores.

12
Peter 12 oct. 2009 a las 07:23