Conozco muy bien a Ruby. Creo que es posible que necesite aprender Python en este momento. Para aquellos que conocen ambos, ¿qué conceptos son similares entre los dos y cuáles 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 "veraces"; ¿Es idiomático usar el equivalente de map
y each
, o murmurar algo sobre una lista de comprensiones mascullar la norma?
Si obtengo una buena variedad de respuestas, me complace agregarlas a una wiki comunitaria. De lo contrario, todos pueden pelear y luchar entre sí para tratar de crear la única lista completa y verdadera.
Editar : Para ser claros, mi objetivo es Python "adecuado" e idiomático. Si hay un equivalente en Python de inject
, pero nadie lo usa porque hay una forma mejor / diferente de lograr la funcionalidad común de iterar 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.
Respuestas:
Aquí hay algunas diferencias clave para mí:
Ruby tiene bloques; Python no lo hace.
Python tiene funciones; Ruby no lo hace. 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 se pueden pasar directamente. En su lugar, debe envolverlos en Proc para pasarlos.
Ruby y Python admiten 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 acceso de escritura. En Ruby, defines cierres usando bloques. Los cierres tienen acceso completo de lectura y escritura a las variables del ámbito externo.
Python tiene listas por comprensión, 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 mayores que 15. En Ruby, tendrías que escribir lo siguiente:
values.select {|v| v > 15}.map {|v| v * v}
El código 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 mayores que 15. Luego, toma la matriz intermedia y genera una matriz final que contiene los cuadrados de los intermedios. A continuación, se desecha la matriz intermedia. Entonces, Ruby termina con 3 matrices en 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 lo hace. En Ruby, debe usar matrices para simular tuplas.
Ruby admite declaraciones switch / case; Python no lo hace.
Ruby admite elexpr ? val1 : val2
operador ternario estándar ; Python no lo hace.Ruby admite solo herencia única. Si necesita imitar la herencia múltiple, puede definir módulos y usar combinaciones para extraer los métodos del módulo en clases. Python admite herencia múltiple en lugar de combinaciones de módulos.
Python solo admite funciones lambda de una sola línea. Los bloques Ruby, que son una especie de funciones lambda, pueden ser arbitrariamente grandes. Debido a esto, el código Ruby generalmente se escribe en un estilo más funcional que el código Python. Por ejemplo, para recorrer una lista en Ruby, normalmente lo hace
collection.each do |value| ... end
El bloque funciona de manera muy similar a una función a la que se le pasa
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 admite este método):def some_operation(value): ... collection.each(some_operation)
Eso no fluye muy bien. Por lo tanto, normalmente se usaría el siguiente enfoque no funcional en Python:
for value in collection: ...
Utilizar los recursos de forma 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 ocurre una excepción.
En Ruby, debido a que los bloques son tan fáciles de usar (ver el n. ° 9), normalmente codificaría este patrón como un método que toma un bloque para que la operación arbitraria se realice en el recurso.
En Python, pasar una función para la acción arbitraria es un poco más torpe ya que tiene que escribir una función interna con nombre (vea el n. ° 9). En cambio, Python usa una
with
declaración para el manejo seguro de recursos. Consulte ¿Cómo limpio correctamente un objeto de Python? para más detalles.fuente
nonlocal
corrige esto 4. Python también le brinda expresiones generadoras (similares a las listas por comprensión, pero no calcule nada hasta que se lo pidan; piense en las listas por comprensión como expresiones generadoras alimentadas alist
(que toma un iterable y devuelve una lista que contiene todo el iterable cedido) - esto puede ahorrar mucho esfuerzo en algunos casos).val1 if expr else val2
. 8. Aunque veo que se usa principalmente para el aumento de estilo mixin.values.map{|v| v*v if v > 15}.compact
. En mi humilde opinión, esto es incluso más expresivo (y ciertamente más claro) que su ejemplo de Python.values.map{|v| v*v if v > 15}.compact!
. Esto significa que solo la lista de entrada y la lista resultante existen en la memoria. Vea el n. ° 4 aquí: igvita.com/2008/07/08/6-optimization-tips-for-ruby-mriAcabo de pasar un par de meses aprendiendo Python después de 6 años de Ruby. Realmente no había una gran comparación entre los dos idiomas, así que decidí trabajar y escribir uno yo mismo. Ahora, se trata principalmente de programación funcional, pero como mencionas el
inject
método de Ruby , supongo que estamos en la misma longitud de onda.Espero que esto ayude: La 'fealdad' de Python
Un par de puntos que lo llevarán a moverse en la dirección correcta:
Toda la bondad de la programación funcional que usa en Ruby está en Python, y es aún más fácil. Por ejemplo, puede mapear funciones exactamente como lo esperaría:
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
para efectos secundarios, el equivalente en Python es el ciclo for:for n in [1, 2, 3]: print n
Las listas por comprensión son excelentes 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 (asumiendo que tiene una función
p()
que devuelve verdadero para los palíndromos), todo lo que necesita es una sola lista de comprensión: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])]
fuente
Class.method
, el método es "independiente" y el primer argumento debe ser unaClass
instancia; cuando escribeobject.method
, el método está "vinculado" a laobject
instancia deClass
. Esto le permite elegir si usar map (etc.) para llamar al método en una instancia diferente cada vez (pasar un método no vinculado), o mantener la instancia fija y pasar un segundo argumento diferente cada vez. Ambos son útiles.[s[x:y] for x in range(l) for y in range(x,l+1) if p(s[x:y])]
- esta línea muestra lo difícil que es leer Python. Cuando lees el código Ruby mueves tus ojos de izquierda a derecha, sin devoluciones. Pero para leer el código de Python, necesitas ir de izquierda a derecha, izquierda, derecha, izquierda, derecha ... y paréntesis, paréntesis, paréntesis, paréntesis ... También en Python a menudo necesitas mezclar métodos y funciones. Es una locura: enE(C(A.B()).D())
lugar de RubyA.B.C.D.E
Mi sugerencia: no intente aprender las diferencias. Aprenda a abordar el problema en Python. Al igual que hay un enfoque Ruby para cada problema (que funciona muy bien dadas las limitaciones y fortalezas del lenguaje), existe un enfoque Python para el problema. ambos son diferentes. Para obtener lo mejor de cada idioma, debe aprender el idioma en sí, y no solo la "traducción" de uno a otro.
Ahora, dicho esto, la diferencia lo ayudará a adaptarse más rápido y a realizar modificaciones en un programa de Python. Y está bien para empezar a escribir. Pero intente aprender de otros proyectos el por qué detrás de las decisiones de arquitectura y diseño en lugar del cómo detrás de la semántica del lenguaje ...
fuente
each
método de Ruby ?" Estoy preguntando "¿En qué se diferencian las cosas correctamente en Python de Ruby y dónde se hacen correctamente?" Si Python lofalse
es en realidadFalse
, es tan importante saber dónde y cuándo debería hacer las cosas de una manera Rubyesca, y dónde y cuándo no debería hacerlo.Conozco al pequeño Ruby, pero aquí hay algunas viñetas sobre las cosas que mencionaste:
nil
, el valor que indica la falta de un valor, seríaNone
(tenga en cuenta que lo verifica comox is None
ox is not None
, no con==
- o por coerción a booleano, vea el siguiente punto).None
, Cero-esque números (0
,0.0
,0j
(número complejo)) y las colecciones vacías ([]
,{}
,set()
, la cadena vacía""
, etc.) se consideran Falsy, todo lo demás se considera Truthy.for
-) se repite explícitamente. Para generar un nuevo grupo de cosas sin efectos secundarios, use listas de comprensión (o sus parientes: expresiones generadoras para iteradores únicos perezosos, comprensiones de dict / set para dichas colecciones).Con respecto al bucle: tiene
for
, que opera en un iterable (! Sin contar), ywhile
, que hace lo que esperaría. El fromer 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 ambos y el valor predeterminado es una lista, lamentablemente). Existen numerosas herramientas para trabajar con iteradores:zip
itera cualquier número de iterables en paralelo,enumerate
le brinda(index, item)
(en cualquier iterable, no solo en listas), ¡incluso cortando iterables abritarios (posiblemente grandes o infinitos)! Descubrí que estos hacen que muchas tareas de bucle sean mucho más simples. No hace falta decir que se integran muy bien con listas por comprensión, expresiones generadoras, etc.fuente
x is None
ox is not None
? Siempre consulto conx == None
yx != None
.x
define__eq__
de una manera tonta, podría dar un falso positivo. Si__eq__
no se programa con suficiente cuidado, podría fallar (por ejemploAttributeError
) cuando se le dan ciertos valores (por ejemploNone
). Por el contrario,is
no se puede anular: siempre compara la identidad del objeto, que es la forma correcta (más sólida, simple y limpia) de verificar un singleton.En Ruby, las variables de instancia y los métodos no están relacionados en absoluto, excepto cuando los relaciona 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 hacer referencia a fx se refiere al objeto del 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.
fuente