2010-09-04 10 views
8

¿Es pitónico imitar la sobrecarga de métodos como se encuentra en los lenguajes estáticos? Con eso me refiero a escribir una función que comprueba los tipos de sus argumentos y se comporta de manera diferente según esos tipos.¿Es pitónico imitar la sobrecarga de métodos?

Aquí se muestra un ejemplo:

class EmployeeCollection(object): 
    @staticmethod 
    def find(value): 
     if isinstance(value, str): 
      #find employee by name and return 
     elif isinstance(value, int): 
      #find employee by employee number and return 
     else: 
      raise TypeError() 
+0

Creo que se le olvidó para agregar el 'self' como el primer parámetro –

+0

En realidad, se suponía que era un método estático. Lo actualicé ahora. – hwiechers

Respuesta

13

No muy Pythonic, excepto tal vez, en 2.6 o mejor, si todos los controles se basan en las nuevas clases de base abstracta, que están destinados, en parte, precisamente para facilitar dicho uso. Si alguna vez se encuentra escribiendo para clases de concreto, entonces usted sabe que está haciendo que su código sea frágil y reduciendo su uso.

Por lo tanto, por ejemplo, comprobar si tiene una instancia de numbers.Integral no es tan malo, que el nuevo ABC existe en gran parte exactamente para facilitar dicha comprobación. Comprobar si tiene una instancia de int es un desastre, descartando long, gmpy.mpz, y un bazillion de otras clases de números enteros, para absolutamente ningún buen propósito: ¡nunca verifique las clases concretas!

Las cadenas son un caso difícil, pero la clase abstracta basestring (no es una de las nuevas clases de ABC) es una posibilidad. Un poco demasiado restrictivo, tal vez, pero si está usando otro abecedario a su alrededor, podría ser un poco, así que trabaje, si realmente tiene que hacerlo. Definitivamente nostr - ¿por qué descartar unicode?!

13

En realidad no, ya que se pierde la capacidad de utilizar los tipos que no son del todo, pero que de cerca, lo suficiente. Cree dos métodos separados (find_by_name() y find_by_number()) en su lugar.

+0

-1, ahora solo está codificando estáticamente tipos en los nombres de sus métodos, ¡no muy dinámicos! ¿Y qué pasa si tienes 5 parámetros diferentes que podrían ser una cadena o un número? ¿Creas 32 métodos diferentes? – Gabe

+3

Si tiene 5 argumentos que podrían ser una cadena o un número, entonces tiene problemas arquitectónicos más grandes (y posiblemente un problema subyacente más profundo que ningún foro de desarrollo de software puede ayudar con ...). –

+0

Debe tener diferentes nombres de métodos porque la implicación con * comprobación * del tipo significa que * actúa de forma diferente * en función del resultado de esa comprobación. En Python, sin embargo, preferimos hacer las cosas explícitas; si el comportamiento es diferente, los nombres de los métodos también deberían ser diferentes. La única vez que consideraría que romper esta regla es pythonic sería verificar que un argumento 'es Ninguno', para invocar un comportamiento predeterminado razonable. – SingleNegationElimination

2

Yo diría que sí, es 'Ptónico' Y hay ejemplos para respaldar esto (que los otros carteles no han dado). ¡Para responder esto correctamente debería haber ejemplos!

Desde núcleo pitón:

  • string.startswith() Se acepta una cadena o una tupla (de cuerdas).
  • string.endswith()

En pymongo:

  • find_one() acepta ya sea un objeto dict para buscar, o se utilice otro otro objeto como el ID.

Lo siento, no sé más, pero creo que hay MUCHOS ejemplos de métodos que se comportan de manera diferente según los parámetros dados. Eso es parte de la belleza de no hacer cumplir los tipos.

+1

-1 typechecking es __never__ pythonic. Verifique las interfaces pero no escriba. la medida en que ocurre la verificación de tipo en la biblioteca estándar debe tomarse como una deficiencia y no como un ejemplo. – aaronasterling

+0

@aaronasterling: Creo que 'nunca' es un poco fuerte. ¿Qué tal en los métodos '__init__'? Por ejemplo, puedes construir un 'bytearray' a partir de otro' bytearray', un entero, un iterable de enteros, un 'memory_view', un string o (solo Python 3) un objeto' bytes'. ¡Todo eso desde un tipo incorporado recientemente agregado! –

+0

@Scott: El hecho de que necesite usar la verificación de tipo allí no hace que el tipo revise más Pythonic. –

1

Una manera más pitónica de llegar a este tipo de funcionalidad es intentarlo y usarlo de la manera preferida (lo que sea que eso signifique) y si el argumento no lo admite, pruebe con una alternativa.

Here'd dos maneras de hacerlo:

class EmployeeCollection(object): 
    def find(value): 
     try: 
      #find employee by name and return 
     catch: 
      try: 
       #find employee by employee number and return 
      catch: 
       raise TypeError() 

pero eso es tipo de asqueroso. así es como generalmente hago esto:

class EmployeeCollection(object): 
    def find(value): 
     if hasattr(value, 'join'): 
      #find employee by name and return 
     elif hasattr(value, '__div__'): 
      #find employee by employee number and return 
     else: 
      raise TypeError() 

En realidad, el atributo real Me echa para depende de lo que ocurre en esos comentarios, voy a prefiero para comprobar si hay un atributo que utilizo realmente.

+0

Diría que su primer enfoque es mejor. – detly

+0

Pase el argumento al constructor del tipo que le interesa y capte cualquier excepción de eso. –

+0

El segundo ejemplo es el más "pitónico" porque está verificando la interfaz, no el tipo. Entonces yo diría que esa es tu respuesta correcta. Creo que un dogma sobre "nunca escribir" es un poco tonto. – Amala

5

No, el tipo de comprobación aquí no es Pythonic. Otra opción si haces múltiples métodos de fantasía no es seguir con un método pero el uso de múltiples parámetros:

def find(name=None, employee_number=None): 
    if sum(x != None for x in (name, employee_number)) != 1: 
     #raise exception - exactly one value should be passed in 
    if name is not None: 
     #find employee by name and return 
    if employee_number is not None: 
     #find employee by employee number and return 

Cuando utilice el destino es tan evidente como con varios métodos:

employee1 = x.find(name="John Smith") 
employee2 = x.find(employee_number=90210) 
Cuestiones relacionadas