programacion - python tutorial
Aprendiendo Python de Ruby; Diferencias y similitudes (5)
Conozco a Ruby muy bien. Creo que puedo necesitar aprender Python actualmente. Para aquellos que conocen ambos, ¿qué conceptos son similares entre los dos y qué son diferentes?
Estoy buscando una lista similar a un manual que escribí para Learning Lua para JavaScripters : cosas simples como significado de espacios en blanco y construcciones de bucle; el nombre de nil
en Python, y qué valores se consideran "verdad"; ¿Es idiomático usar el equivalente de un map
y each
, o son murmuraciones sobre las comprensiones burlonas de la norma?
Si recibo una buena variedad de respuestas, me complace agregarlas a una wiki de la comunidad. O bien, todos ustedes pueden luchar y protegerse mutuamente para tratar de crear la única y completa lista completa.
Editar : Para ser claros, mi objetivo es "apropiado" y idiomático Python. Si hay un equivalente de Python de inject
, pero nadie lo usa porque hay una manera mejor / diferente de lograr la funcionalidad común de repetir una lista y acumular un resultado en el camino, quiero saber cómo se hacen las cosas. Tal vez actualice esta pregunta con una lista de objetivos comunes, cómo los logra en Ruby, y pregunte cuál es el equivalente en Python.
Acabo de pasar un par de meses aprendiendo Python después de 6 años de Ruby. Realmente no hubo una gran comparación para los dos idiomas, así que decidí levantarme y escribir uno yo mismo. Ahora, se trata principalmente de la programación funcional, pero como mencionas el método de inject
de Ruby, supongo que estamos en la misma onda.
Espero que esto ayude: La ''fealdad'' de Python
Un par de puntos que te ayudarán a avanzar en la dirección correcta:
Toda la bondad de programación funcional que utilizas en Ruby está en Python, y es aún más fácil. Por ejemplo, puede asignar funciones exactamente como espera:
def f(x): return x + 1 map(f, [1, 2, 3]) # => [2, 3, 4]
Python no tiene un método que actúe como
each
. Como solo usaeach
de los efectos secundarios, el equivalente en Python es el ciclo for:for n in [1, 2, 3]: print n
Las comprensiones de listas son geniales cuando a) tienes que lidiar con funciones y colecciones de objetos juntas yb) cuando necesitas iterar usando múltiples índices. Por ejemplo, para encontrar todos los palíndromos en una cadena (suponiendo que tienes una función
p()
que devuelve verdadero para palíndromos), todo lo que necesitas es una sola comprensión de la lista:s = ''string-with-palindromes-like-abbalabba'' l = len(s) [s[x:y] for x in range(l) for y in range(x,l+1) if p(s[x:y])]
Aquí hay algunas diferencias clave para mí:
Ruby tiene bloques; Python no.
Python tiene funciones; Ruby no. En Python, puede tomar cualquier función o método y pasarlo a otra función. En Ruby, todo es un método y los métodos no pueden pasarse directamente. En cambio, debes envolverlos en Proc para pasarlos.
Ruby y Python son compatibles con cierres, pero de diferentes maneras. En Python, puede definir una función dentro de otra función. La función interna tiene acceso de lectura a las variables de la función externa, pero no al acceso de escritura. En Ruby, defines cierres usando bloques. Los cierres tienen acceso completo de lectura y escritura a las variables del alcance externo.
Python tiene listas de comprensiones, que son bastante expresivas. Por ejemplo, si tiene una lista de números, puede escribir
[x*x for x in values if x > 15]
para obtener una nueva lista de los cuadrados de todos los valores superiores a 15. En Ruby, tendría que escribir lo siguiente:
values.select {|v| v > 15}.map {|v| v * v}
El código de Ruby no se siente tan compacto. Tampoco es tan eficiente ya que primero convierte la matriz de valores en una matriz intermedia más corta que contiene los valores superiores a 15. Luego, toma la matriz intermedia y genera una matriz final que contiene los cuadrados de los intermedios. La matriz intermedia se descarta. Entonces, Ruby termina con 3 matrices en la memoria durante el cálculo; Python solo necesita la lista de entrada y la lista resultante.
Python también proporciona comprensiones de mapas similares.
Python admite tuplas; Ruby no. En Ruby, debes usar matrices para simular tuplas.
Ruby admite declaraciones de switch / case; Python no.
Ruby admite el estándarexpr ? val1 : val2
expr ? val1 : val2
operador ternario; Python no.Ruby solo admite una sola herencia. Si necesita imitar una herencia múltiple, puede definir módulos y usar mezclas para extraer los métodos del módulo en clases. Python admite herencia múltiple en lugar de módulos mix-ins.
Python solo admite funciones lambda de línea única. Los bloques de Ruby, que son una especie de / tipo de funciones lambda, pueden ser arbitrariamente grandes. Debido a esto, el código de Ruby generalmente se escribe en un estilo más funcional que el código de Python. Por ejemplo, para recorrer una lista en Ruby, normalmente haces
collection.each do |value| ... end
El bloque funciona muy parecido a una función que se pasa a
collection.each
. Si tuviera que hacer lo mismo en Python, tendría que definir una función interna con nombre y luego pasarla a la colección de cada método (si la lista es compatible con este método):def some_operation(value): ... collection.each(some_operation)
Eso no fluye muy bien. Entonces, típicamente, el siguiente enfoque no funcional se usaría en Python:
for value in collection: ...
Usar los recursos de manera segura es bastante diferente entre los dos idiomas. Aquí, el problema es que desea asignar algún recurso (abrir un archivo, obtener un cursor de base de datos, etc.), realizar alguna operación arbitraria en él y luego cerrarlo de manera segura incluso si se produce una excepción.
En Ruby, debido a que los bloques son tan fáciles de usar (ver n. ° 9), normalmente codificaría este patrón como un método que requiere un bloque para que la operación arbitraria funcione en el recurso.
En Python, pasar una función para la acción arbitraria es un poco más lento ya que tiene que escribir una función interna con nombre (ver # 9). En cambio, Python usa una instrucción
with
para un manejo seguro de los recursos. Consulte ¿Cómo puedo limpiar correctamente un objeto de Python? para más detalles.
Conozco al pequeño Ruby, pero aquí hay algunos puntos acerca de las cosas que mencionas:
-
nil
, el valor que indica la ausencia de un valor, seríaNone
(observe que lo comprueba comox is None
ox is not None
, no con==
- o por coerción a booleano, vea el siguiente punto). -
None
, números cero-esque (0
,0.0
,0j
(número complejo)) y colecciones vacías ([]
,{}
,set()
, la cadena vacía""
, etc.) se consideran falsos, todo lo demás se considera verdad. - Para efectos secundarios, (
for
-) loop explícitamente. Para generar un nuevo grupo de cosas sin efectos secundarios, use listas de comprensión (o sus parientes, expresiones generadoras para iteradores perezosos de una sola vez, comprensión dictada / establecida para dichas colecciones).
Con respecto al bucle: tiene for
, que opera en un iterable (! Sin contar), y while
, que hace lo que cabría esperar. El remitente es mucho más poderoso, gracias al amplio soporte para iteradores. No solo casi todo lo que puede ser un iterador en lugar de una lista es un iterador (al menos en Python 3: en Python 2, tienes ambas cosas y la lista predeterminada es una lista, lamentablemente). Existen numerosas herramientas para trabajar con iteradores: zip
itera cualquier número de iterables en paralelo, enumerate
te da (index, item)
(en cualquier iterable, no solo en las listas), incluso corta retazos abritary (posiblemente grandes o infinitos). Descubrí que esto hace que muchas tareas de bucle sean mucho más simples. No hace falta decir que se integran muy bien con listas de comprensión, expresiones de generador, etc.
En Ruby, las variables de instancia y los métodos no tienen ninguna relación, excepto cuando los relacionas explícitamente con attr_accessor o algo así.
En Python, los métodos son solo una clase especial de atributo: uno que es ejecutable.
Así por ejemplo:
>>> class foo:
... x = 5
... def y(): pass
...
>>> f = foo()
>>> type(f.x)
<type ''int''>
>>> type(f.y)
<type ''instancemethod''>
Esa diferencia tiene muchas implicaciones, como por ejemplo que la referencia a fx se refiere al objeto de método, en lugar de llamarlo. Además, como puede ver, fx es público por defecto, mientras que en Ruby, las variables de instancia son privadas por defecto.
Mi sugerencia: no trates de aprender las diferencias. Aprenda cómo abordar el problema en Python. Al igual que hay un enfoque Ruby para cada problema (que funciona muy bien dando las limitaciones y fortalezas del lenguaje), hay un enfoque de Python para el problema. ambos son diferentes Para obtener lo mejor de cada idioma, realmente debe aprender el idioma en sí, y no solo la "traducción" de uno a otro.
Ahora, dicho esto, la diferencia te ayudará a adaptarte más rápido y hacer modificaciones de 1 a un programa de Python. Y eso está bien para comenzar a escribir. Pero trate de aprender de otros proyectos el por qué detrás de las decisiones de arquitectura y diseño en lugar de cómo detrás de la semántica del lenguaje ...