while switch make implementar how hacer equivalente como python switch-statement

make - use switch in python



Elegir entre diferentes reemplazos de mayúsculas y minúsculas en Python-dictionary o if-elif-else? (7)

Recientemente leí las preguntas que recomiendan no usar declaraciones de cambio de caso en idiomas que sí lo admitan. En lo que respecta a Python, he visto varios reemplazos de cajas de interruptores, como:

  1. Usando un diccionario (muchas variantes)
  2. Usando un tuple
  3. Usando un decorador de funciones ( http://code.activestate.com/recipes/440499/ )
  4. Uso de polimorfismo (método recomendado en lugar de tipos de comprobación de objetos)
  5. Usando una escalera if-elif-else
  6. Alguien incluso recomendó el patrón de visitante (posiblemente extrínseco)

Dada la gran variedad de opciones, me resulta un poco difícil decidir qué hacer para un código en particular. Me gustaría conocer los criterios para seleccionar uno de estos métodos sobre el otro en general. Además, agradecería consejos sobre qué hacer en los casos específicos en los que tengo problemas para decidir (con una explicación de la elección).

Aquí está el problema específico:
(1)

def _setCurrentCurve(self, curve): if curve == "sine": self.currentCurve = SineCurve(startAngle = 0, endAngle = 14, lineColor = (0.0, 0.0, 0.0), expansionFactor = 1, centerPos = (0.0, 0.0)) elif curve == "quadratic": self.currentCurve = QuadraticCurve(lineColor = (0.0, 0.0, 0.0))

Este método es llamado por una ranura qt en respuesta a la elección de dibujar una curva desde un menú. El método anterior contendrá un total de 4-7 curvas una vez que la aplicación esté completa. ¿Está justificado utilizar un diccionario desechable en este caso? Dado que la forma más obvia de hacer esto es if-elif-else, ¿debería seguir con eso? También he considerado usar ** kargs aquí (con la ayuda de amigos) ya que todas las clases de curvas usan ** kargs ...

(2)
Esta segunda parte del código es una ranura qt que se llama cuando el usuario cambia una propiedad de una curva. Básicamente, la ranura toma los datos de la interfaz gráfica de usuario (spinBox) y los coloca en una variable de instancia de la clase de curva apropiada. En este caso, nuevamente tengo la misma pregunta: ¿debo usar un dict?

Aquí está la ranura mencionada

def propertyChanged(self, name, value): """A Qt slot, to react to changes of SineCurve''s properties.""" if name == "amplitude": self.amplitude = value elif name == "expansionFactor": self.expansionFactor = value elif name == "startAngle": self.startAngle = value elif name == "endAngle": self.endAngle = value

Para referencia, aquí está el código para conectarse a la ranura anterior:

def _connectToPage(self, page): for connectionData in page.getConnectibles(): self.connect(connectionData["object"], SIGNAL(connectionData["signal"]), lambda value, name = connectionData["property"]:/ self.currentCurve.propertyChanged(name, value)) self.connect(connectionData["object"], SIGNAL(connectionData["signal"]), self.hackedDisplayArea.update)

Nota : el self.endAngle etc. se inicializa en el constructor.

Por lo que sé, las razones para elegir un dictado son las búsquedas rápidas. ¿Cuándo se justifica eso? cuando tengo 100 casos o mas? ¿Es una buena idea seguir creando y desechando un diccionario cada vez que se llama a la función? Si construyo un dictado para este propósito fuera de una función, ¿debo verificar si es necesario en algún lugar? ¿Qué pasa si no es necesario en otro lugar?

Mi pregunta es ¿cuál es la mejor práctica si hay una? ¿Cuál es la forma mejor / más elegante de hacer las cosas? Incluya otra forma, cuándo usar if-elif-else , cuándo usar cada una de las otras opciones?


Cada una de las opciones expuestas se ajustan bien a algunos escenarios:

  1. if-elif-else: simplicidad, claridad
  2. diccionario: útil cuando lo configura dinámicamente (imagine que necesita una funcionalidad particular para ejecutarse en una rama)
  3. Tupla: simplicidad sobre el caso if-else para múltiples opciones por rama.
  4. polimorfismo: ramificación orientada a objetos automática
  5. etc.

Python tiene que ver con la legibilidad y la consistencia, e incluso si tu decisión siempre será subjetiva y dependerá de tu estilo, siempre debes pensar en los mantras de Python.

./alex


Con respecto a sus preguntas de diccionario:

Por lo que sé, las razones para elegir un dictado son las búsquedas rápidas. ¿Cuándo se justifica eso? cuando tengo 100 casos o mas? ¿Es una buena idea seguir creando y desechando un diccionario cada vez que se llama a la función? Si construyo un dictado para este propósito fuera de una función, ¿debo verificar si es necesario en algún lugar? ¿Qué pasa si no es necesario en otro lugar?

  1. Otro tema es la mantenibilidad. Tener el diccionario string-> curveFunction le permite manejar el menú de datos. Luego, agregar otra opción es solo una cuestión de poner otra entrada string - function en el diccionario (que se encuentra en una parte del código dedicado a la configuración).

  2. Incluso si tiene solo unas pocas entradas, se "separa las preocupaciones"; _setCurrentCurve es responsable de cablear en tiempo de ejecución, no de definir la caja de componentes.

  3. Construye el diccionario y mantenlo en él.

  4. Incluso si no se utiliza en otros lugares, obtiene los beneficios anteriores (localización, mantenimiento).

Mi regla de oro es preguntar "¿Qué está pasando aquí?" para cada componente de mi código. Si la respuesta es de la forma.

... y ... y ...

(como en "definir la biblioteca de funciones y asociar cada una con un valor en el menú") entonces hay algunas preocupaciones que piden ser separadas.


En Python, no piense en cómo reemplazar una declaración de cambio.

Usa clases y polimorfismo en su lugar. Trate de mantener la información sobre cada opción disponible y cómo implementarla en un solo lugar (es decir, la clase que la implementa).

De lo contrario, terminará teniendo muchos lugares que contienen una pequeña fracción de cada opción, y la actualización / ampliación será una pesadilla de mantenimiento.

Este es exactamente el tipo de problema que OOD intenta resolver por medio de la abstracción, la ocultación de la información, el polimorfismo y el lote.

Piense en las clases de objetos que tiene y sus propiedades, luego cree una arquitectura OO a su alrededor. De esta manera, nunca más tendrá que preocuparse por una declaración de "cambio" faltante.


En el primer ejemplo ciertamente me quedaría con la afirmación if-else. De hecho, no veo una razón para no usar if-else a menos que

  1. Usted encuentra (utilizando, por ejemplo, el módulo de perfil) que la instrucción if es un cuello de botella (IMO muy improbable a menos que tenga un gran número de casos que hagan muy poco)

  2. El código que usa un diccionario es más claro / tiene menos repetición.

Tu segundo ejemplo realmente reescribiría

setattr(self, name, value)

(Probablemente agregando una declaración de afirmación para capturar nombres inválidos).


Estoy de acuerdo con df con respecto al segundo ejemplo. El primer ejemplo probablemente trataría de volver a escribir usando un diccionario, particularmente si todos los constructores de curvas tienen la misma firma de tipo (tal vez usando * args y / o ** kwargs). Algo como

def _setCurrentCurve(self, new_curve): self.currentCurve = self.preset_curves[new_curve](options_here)

o tal vez incluso

def _setCurrentCurve(self, new_curve): self.currentCurve = self.preset_curves[new_curve](**preset_curve_defaults[new_curve])


Suspiro. Demasiado retorcimiento de manos sobre la parte incorrecta del problema. La declaración de cambio no es el problema. Hay muchas formas de expresar "alternativas" que no agregan significado .

El problema es el significado , no las opciones de declaración técnica.

Hay tres patrones comunes.

  • Mapeo de una clave a un objeto . Use un diccionario si es casi totalmente estático y tiene un mapeo entre una clave simple y otra cosa más compleja. Crear un diccionario sobre la marcha cada vez que lo necesites es una tontería. Puede usar esto si es lo que quiere decir : sus "condiciones" son valores clave simples y estáticos que se asignan a objetos.

  • Variante de comportamiento entre subclases . Utilice polimorfismo en lugar de escribir objetos de comprobación. Correcto. Si tiene objetos similares en varias clases con comportamiento de variante, deben ser polimórficos. Use esto tan a menudo como sea posible.

  • Otras variantes de comportamiento . Use una escalera if-elif-else. Use esto cuando no tenga una asignación de clave a valor en gran parte estática. Use esto cuando las condiciones son complejas, o quiere decir procedimientos, no objetos.

Todo lo demás es un código complicado que puede lograr resultados similares.

Usando una tupla. Esto es solo un diccionario sin el mapeo. Esto requiere búsqueda, y la búsqueda debe evitarse siempre que sea posible. No hagas esto, es ineficiente. Usa un diccionario.

Usando un decorador de funciones ( http://code.activestate.com/recipes/440499/ ). Icky. Esto oculta la naturaleza if-elif-elif del problema que estás resolviendo. No hagas esto, no es obvio que las opciones sean exclusivas . Usa cualquier otra cosa.

Alguien incluso recomendó el patrón de visitante . Úselo cuando tenga un objeto que sigue el patrón de diseño compuesto . Esto depende del polimorfismo para que funcione, por lo que no es realmente una solución diferente.


Teniendo en cuenta que esto se hace en respuesta a una acción del usuario (seleccionando algo de un menú), y la cantidad de opciones que anticipa es muy pequeña, definitivamente me quedaría con una escalera simple if-elif-else.

No tiene sentido optar por la velocidad, ya que solo sucede tan rápido como el usuario puede hacer la selección de todos modos, esto no es el "bucle interno de un trazador de rayos", es decir. Claro, es importante dar una respuesta rápida al usuario, pero como el número de casos es muy pequeño, tampoco existe peligro.

No tiene sentido optimizar la concisión, ya que, de todos modos, la escala (ifo clearer, cero legibilidad-sobrecarga) si-ladder será muy corta.