[Python-es] Análisis sobre Python vs ruby WAS: Python y/o ruby para no programadores

Olemis Lang (Simelix) olemis+py en gmail.com
Mar Feb 9 15:52:15 CET 2010


Antes q todo, por lo menos mi intención no es comenzar un flamewar,
sino analizar las potencialidades, facilidades o como quiera q le
llamemos de ambos lenguajes . Aclaro q prefiero Python, pero q también
me gusta Ruby ;o)

2010/2/9 lasizoillo <lasizoillo en gmail.com>:
> El día 9 de febrero de 2010 14:09, Olemis Lang (Simelix)
> <olemis+py en gmail.com> escribió:
>> Solo una precisión ;o)
>>
>> 2010/2/8 Chema Cortes <pych3m4 en gmail.com>:
>>> El día 8 de febrero de 2010 16:39, Olemis Lang (Simelix)
>>> <olemis+py en gmail.com> escribió:
>>>
>>>> pero los bloques de código de Ruby son muy cómodos y potentes (y no
>>>> hay nada como eso en Python)
>>>
>>> Cómodos sí, pero no diría tanto como que no hay nada en python. En
>>> python puedes hacer casi lo mismo con iterables, con la posibilidad de
>>> usar la sencillez de la comprensión de listas para manejarlos (compara
>>> código anidando bloques de código, por ejemplo).
>>>
>>
>> No creo estar completamente de acuerdo. Hay varias cuestiones entre
>> ambos enfoques q creo q hay que destacar :
>>
>>  - Si el tema es sobre el uso de bloques con `each` entonces esto
>>     es completamente corecto
>>  - Pero los bloques no se limitan necesariamente a eso.
>>     - Por ejemplo,
>>       en el caso de IO asíncrona muy frecuentemente se utilizan
>>       callbacks. En Python la única solución es escribir una
>>       función o «clase invocable» (i.e. callable) y pasarla como parámetro
>>       a alguna función o mecanismo de registro q tenga la API.
>>       Esto puede ser engorroso. En Ruby los casos sencillos pueden
>>       ser descritos con un bloque de código inline y queda más legible
>>       y menos «verbose» (más conciso).
>
> #Asi a ojo y sin probar
>
> def block(code):
>   def inner():
>       exec code
>   return inner
>
> cosa_asincrona.register(block("print 'no es para tanto'"))
> otra_cosa.register(block("""
>    for i in range(10):
>        print 'no volvere a copiar en clase'
> """))
>
> No lo he probado porque soy feliz definiendo funciones para las
> llamadas asincronas.

Sería curioso saber si cuando Ud lo ejecuta le funciona. Pongamos el
ejemplo de cuando se hace el exec en un módulo con una cadena definida
en otro, digamos porq Ud ofrece `block` en un módulo y especifica lo q
hay q hacer en otro. En este caso el namespace global no coincide con
el q Ud espera y entonces solo obtendrá un `NameError` tras otro y
tendrá que apelar al `stack` para saber de dónde lo llamaron y, y ...
y ya no sería tan simple (o podríamos forzar al cliente de la librería
a estar declarando `block` en cada módulo q él haga, y si cambia algo
en el framework, como hay gran dispersión del código «scattering», y
como eso es casi el anti patrón más malo en la ingeniería de software,
el mantenimiento del código no será nada divertido). E.g. me viene a
la mente esto porq me sucedió con CASPy cuando declaraba «inline
assertions».

De todas formas no estoy en contra de un estilo ni de otro, solo q
IMO, los bloques inline podrían aportar legibilidad en algunos casos
;o).

> Hay no, perdona, que tambien puedes consumir las
> llamadas asincronas con coroutinas:
> http://blog.mekk.waw.pl/archives/14-Twisted-inlineCallbacks-and-deferredGenerator.html
>

Si el framework lo permite ;o)

De todas formas hay otras soluciones más allá de corutinas q también
ayudan a compensar la carencia de los bloques inline , pero q IMO no
cubren todos los casos . Especialmente los context managers . El
ejemplo, la función `assertRaises` de `unittest`. Una pregunta simple.
¿Qué es más legible? (ambos ejemplos escritos a la carrera y sin
probar ;o) :

{{{
#!python

# Antes de 2.7
class TestCase
  def _helper_method(self, x, b=33, c=12)
     x = 0
     x = b + c / x

  def test_xxx(self):
    self.assertRaises(ZeroDivisionError, self._helper_method, 1, c=4)
}}}

{{{
#!python

# Para 2.7
class TestCase
  def test_xxx(self):
    with self.assertRaises(ZeroDivisionError):
      x = 0
      x = 33 + 4 / x
}}}

No quiere decir esto q me guste todo lo que hay en unittest 2.7 [1]_
pero sí q esto en específico está genial y mucho más legible al tener
el bloque de código inline ;o)

Mi opinión es q las funciones surgieron, fundamentalmente, como un
mecanismo de reutilización. Que puedan ser utilizadas para sustituir
carencias del lenguaje, es válido, pero todavía las carencias están
ahí

;o)

>>    - Otro ejemplo, la solución al `case` o `switch` de Python basada en dict(s)
>>      implica q a cada llave se le asigne algo q, al ejecutarlo, se realiza lo
>>      q sea específico de esa alternativa. Pasa algo más o menos semejante,
>>      en Python resulta engorroso escribir una función para cada alternativa,
>>      sin el propósito de reutilizarla (sino solo para suplir una carencia del
>>      lenguaje) y la legibilidad es pésima, porq todo está separado y
>>      disperso y con un vistazo no se puede tener idea de lo
>>      q pasa. Con bloques inline como los de Ruby se podría mejorar esto.
>>
>
> Si el if/elif/else no te vale, igual si que deberias considerar usar
> funciones separadas :-O
>

Ok, lo cual sería mucho más legible que algo como (en un Python
Rubizadoribilistizado ;o)

{
  'k' : do
          x = 1; y = 2
        end,
  'w' : do
          x = 3; y = 4
        end,
  'g' : do
          x = 5; y = 6
        end,
  'xxx' : do
          x = 7; y = 8
        end,
}

... verdad ? Si se da cuenta, utilizando funciones tendríamos q hacer
4 que retornaran una tupla y se le asignaran a x y y. La otra cuestion
es la de los parámetros de esas funciones. Generalmente se necesitan
cosas en el namespace local para utilizarlas dentro del `switch` y
estas hay q pasarlas como parámetros. Claro, se pueden usar las
clausuras, pero reflexionemos todos los conceptos q se han mencionado
y cuan similares son a lo q nosotros pensamos cuando vamos a tomar uno
de varios caminos para tomar una decisión.

Todos los días digo : A las 8:00am veo los muñe, a las 12:00m las
noticias, a las 6:00 pm los deportes y a las 10:00 pm películas con
sexo y violencia. ¡¡¡ Qué bueno q a las 5:00 am ya tenía hechas las
clausuras !!!

;o)

>> Claro, q en todos estos casos se pudiera utilizar un objeto de typo
>> `code`, la función `compile`, o `exec`, pero el efecto no es el mismo
>> ... IMO
>
> ¿Por el coloreado de sintaxis en el editor?
>

No, de hecho no uso nada eso, todo lo programo con VIm sin colorcitos :P

Lo digo porq errores en el código se detectan al compilar hacia .PYC,
mientras que errores dentro de una cadena ejecutada con exec se
detectan al ejecutar la línea de código del exec (i.e. el hecho de q
compile no quiere decir q no está roto ;o). Además, si las
continuaciones son «perjudiciales como el goto», no creo q un exec sea
más beneficioso, considerando que rompe la secuencia precondición,
invariante, postcondición y las buenas prácticas de escritura del
código q plantearon Dijkstra, CAR Hoare et al. basados en modelos como
CSP, o luego DbC, ... Pero bueno, esa meta-tranca no la sigo para no
ponerme demasiado OT

;o)

> Creo que esto se empieza a perecer a una lucha dialectica entre si la
> tortilla de patata está más buena con cebolla o sin ella :-P

Puede ser q ese sea el camino q otros quieran darle a la conversación,
yo solo quiero explorar y análizar las capacidades del lenguaje
tratando de estar lo menos prejuiciado posible por el hecho de q me
gusta, para así poder ser medianamente objetivo y 30% acertado, y
tener bien claro cómo es posible mejorarlo ;o)

-- 
Regards,

Olemis.

Blog ES: http://simelo-es.blogspot.com/
Blog EN: http://simelo-en.blogspot.com/

Featured article:
Embedding pages? - Trac Users | Google Groups  -
http://feedproxy.google.com/~r/TracGViz-full/~3/-XtS7h-wjcI/e4cf16474aa3cb87



Más información sobre la lista de distribución Python-es