Aprendiendo Python de Ruby; Diferencias y similitudes

131

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 nilen Python y qué valores se consideran "veraces"; ¿Es idiomático usar el equivalente de mapy 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.

Phrogz
fuente
1
lo único que leí fue c2.com/cgi/wiki?PythonVsRuby , realmente no me gusta yo y las sangrías, pero me acostumbré :)
Saif al Harthi
1
Relacionado: stackoverflow.com/questions/1113611/… (No estoy muy seguro de si es un duplicado, ya que esa pregunta pide cosas sin equivalente).
19
@SilentGhost No estoy totalmente de acuerdo. Estoy preguntando "¿Qué es lo mismo entre los idiomas y qué es diferente?" Como se muestra en muchas de las respuestas a continuación, existen respuestas muy claras y útiles posibles para esto.
Phrogz
3
@Phrogz: Lo veo y hace que la pregunta no pueda responderse.
SilentGhost
2
@Phrongz - Para hacerme eco de lo que dije sobre el meta tema que publicaste, el problema con esta pregunta es que el espacio del problema es demasiado grande; es un tema demasiado grande para una sola pregunta. Hay miles de diferencias entre los dos idiomas.
Adam Davis

Respuestas:

153

Aquí hay algunas diferencias clave para mí:

  1. Ruby tiene bloques; Python no lo hace.

  2. 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.

  3. 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.

  4. 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.

  5. Python admite tuplas; Ruby no lo hace. En Ruby, debe usar matrices para simular tuplas.

  6. Ruby admite declaraciones switch / case; Python no lo hace.

  7. Ruby admite el expr ? val1 : val2operador ternario estándar ; Python no lo hace.

  8. 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.

  9. 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:
      ...
    
  10. 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 withdeclaración para el manejo seguro de recursos. Consulte ¿Cómo limpio correctamente un objeto de Python? para más detalles.

Clint Miller
fuente
2
3. Python 3 nonlocalcorrige 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 a list(que toma un iterable y devuelve una lista que contiene todo el iterable cedido) - esto puede ahorrar mucho esfuerzo en algunos casos).
25
7. Sí, lo hace. val1 if expr else val2. 8. Aunque veo que se usa principalmente para el aumento de estilo mixin.
2
@ClintMiller Whoa, ¿sin cambio / caso? Entonces, ¿cuál es la forma sugerida de lograr una funcionalidad similar en Python? si / si no / si?
Phrogz
15
Su ejemplo de rubí en el n. ° 4 no es idiomático. Sería más rubí (y legible) de escribir 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.
sml
10
Además de lo anterior, el uso de! versión de la función compacto evita una copia de la matriz: 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-mri
sml
27

Acabo 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 injectmé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 usa eachpara 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])]
    
David J.
fuente
3
Suspiro, leí esa publicación y confirma mi sospecha: pocas personas entienden el papel y la utilidad de los métodos especiales en Python. Son increíblemente útiles y estandarizados, y se subrayan así para evitar conflictos de nombres con las funciones integradas que a menudo implementan. Nadie que realmente conozca Python está tratando de desalentar su uso.
Rafe Kettler
5
No parece entender cómo funcionan los métodos. Un método es, esencialmente, una función cuyo primer argumento es una instancia de la clase a la que pertenece el método. Cuando escribe Class.method, el método es "independiente" y el primer argumento debe ser una Classinstancia; cuando escribe object.method, el método está "vinculado" a la objectinstancia de Class. 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.
LaC
2
Tienes razón, no entendí cómo funcionaban. Desde que publiqué el artículo, lo entendí mejor. ¡Gracias!
David J.
10
[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: en E(C(A.B()).D())lugar de RubyA.B.C.D.E
Nakilon
2
@Nakilon Es por eso que solo debes usar listas de comprensión anidadas para casos realmente simples, y no como los anteriores. Puede ser "inteligente" escribir una sola línea que encuentre todos los palíndromos en una cadena, pero es mejor reservarlo para el golf de código. Para el código real que alguien más tiene que leer más tarde, simplemente escribiría una función de algunas líneas. Entonces sí, esa línea es difícil de leer, pero eso es culpa del programador, no del lenguaje.
Cam Jackson
10

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 ...

ircmaxell
fuente
7
Agradezco tu sugerencia. Estoy totalmente de acuerdo con el sentimiento (que interpreto como "Aprende a programar Python idiomático") . Eso es precisamente lo que estoy tratando de hacer. No estoy preguntando "¿Cuál es el nombre de Python para el eachmétodo de Ruby ?" Estoy preguntando "¿En qué se diferencian las cosas correctamente en Python de Ruby y dónde se hacen correctamente?" Si Python lo falsees en realidad False, 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.
Phrogz
2
@Phrogz: Eso es justo. La forma en que interpreté su pregunta fue: Hagamos una lista de las diferencias para que podamos cambiar el lenguaje en el que estamos programando . Pero es una pregunta justa. Supongo que interpreté mal lo que pedías.
Dejaré
Estoy aprendiendo python y ruby ​​al mismo tiempo, y en el desarrollo de aplicaciones web veo más similitudes que diferencias.
WesternGun
8

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ía None(tenga en cuenta que lo verifica como x is Noneo x 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.
  • Para efectos secundarios, ( 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), y while, 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: zipitera cualquier número de iterables en paralelo, enumeratele 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
2
Las expresiones generadoras son geniales. Le dan a Python un poco de las capacidades de evaluación perezosa de lenguajes como Haskell.
Clint Miller
@Clint: Sí. Y los generadores completos son aún más capaces (aunque no son necesarios para casos simples, que resultan ser la mayoría).
¿Por qué consultar con x is Noneo x is not None? Siempre consulto con x == Noney x != None.
John
@John: Si se xdefine __eq__de una manera tonta, podría dar un falso positivo. Si __eq__no se programa con suficiente cuidado, podría fallar (por ejemplo AttributeError) cuando se le dan ciertos valores (por ejemplo None). Por el contrario, isno 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.
1
@Juan. "x es Ninguno" es la forma absolutamente idiomática de hacerlo. python.net/~goodger/projects/pycon/2007/idiomatic/handout.html
tokland
6

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.

Paul Prescod
fuente
2
En realidad, lo diría aún más crudamente: en Python, los métodos son solo un tipo particular de atributo, mientras que en Ruby, los atributos son solo un tipo particular de método. Algunas de las características contrastantes importantes entre las dos lenguas caen fuera de este: Funciones y calidad en Python, y el uniforme de principio, el acceso en Ruby
philomory