Do you want to switch to Platzi in English?
11

Lo que extraño de Ruby como desarrollador Python

13431Puntos

hace un año

Disclamer: Esta comparación viene de mi experiencia personal y es subjetiva.

He tenido la oportunidad de trabajar con ambos lenguajes, Ruby y Python, y aunque no puedo decir que un lenguaje sea mejor que el otro, sin duda hay características en cada lenguaje que lo hacen destacar. En este articulo les hablo acerca de los features que extraño de Ruby y que me gustaría ver en Python.

Manejo de dependencias

En Ruby puedes definir tus dependencias de desarrollo, de pruebas y de producción en un solo archivo, el Gemfile. Dentro de este puedes además definir cosas como la fuente de las gemas (dependencias) o agregar condiciones para instalar dichas gemas y cuando las instalas se genera un archivo gemfile.lock con las versiones exactas que todos en el proyecto pueden usar para que todos tengan la version exacta actual de las dependencias.

Ejemplo de gemfile:

source 'https://rubygems.org'unless ENV['QUICK']
gemspec

gem 'rake'
gem 'rack', git:'https://github.com/rack/rack.git'
gem 'rack-test', '>= 0.6.2'
gem "minitest", "~> 5.0"
gem 'yard'

gem "rack-protection", path:"rack-protection"
gem "sinatra-contrib", path:"sinatra-contrib"

gem 'wirble', :group => :development
gem 'debugger', :group => [:development, :test]
...

En Python no hay equivalente al gemfile, solo tenemos un único archivo requements.txt donde se especifican las dependencias. Si queremos dependencias por ambiente, debemos generar varios archivos. Si especificas un rango de versiones en vez de una version exacta, no hay garantías de que dos personas instalando un mismo proyecto tengan las mismas versiones (por eso la buena practica es siempre definir las dependencias exactas).

Ejemplo de un archivo requirements.txt:

Flask==0.8
Jinja2==2.6
Werkzeug==0.8.3
certifi==0.0.8
chardet==1.0.1
distribute==0.6.24
gunicorn==0.14.2
requests>=0.11.1

Hay varios proyectos que buscan mejorar esto, pero nada realmente estándar.

Saber cuáles son y dónde están definidas las dependencias de Ruby es muy fácil, solo hay que ver el gemfile y el gemfile.lock. En un proyecto Python hay que revisar un poco más: usualmente son 4 archivos, un commons.txt, un production.txt, un test.txt, y el requirements.txt. Es más complicado saber qué dependencias son por cada ambiente.

Convenciones en variables de clase y de instancia

Para acceder a las variables del objeto o de clase se debe hacer a través de una variable que se define comúnmente como self (para variables de clase usan cls o klass), lo que trae como consecuencia tener self por todas partes y que todos los métodos tengan siempre el parámetro de función self.

def__init__(self, a, b):self.a = a
    self.b = b
    self.minimum = min(self.minimum, self.maximum)
    self.maximum = max(self.minimum, self.maximum)

Ruby no tiene ese problema porque sigue una convención simple de @<variable> para las variables de instancia y @@<variable> para las variables de clase.

def initialize(a, b)
  @a = a
  @b = b
  @minimum = [min, max].min
  @minimum = [min, max].maxend

Actualizaciones

Actualizar Python 2.7 a 3.x es increíblemente doloroso, a Instagram le costo varios meses en migrar a Python 3, en Ruby:

rvmupgrade 2.1.1 2.1.2

En realidad no es tan fácil si tienes Rails, debes también instalar dependencias compatibles, pero el cambio entre versiones jamás a sido tan brusco.

Formateo de strings

El zen de Python dice entre otras cosas

There should be one – and preferably only one – obvious way to do it

Sin embargo para formateo de strings en Python no se cumple, hay muchas formas de formatear un string.

name = "guido"
print("hello %s" % name ) # hello guido
print("hello {}".format(name)) # hello guido
print("hello {name}".format(name=name)) # hello guido
print(f'hello {name}') # hello guidoclassPerson:def__init__(self, name):
        self.name = name

    def__format__(self, format):if (format == 'hello'):
            return"hello " + self.name # :')return'Hello everyone'
guido = Person(name='guido')

print('{:hello}'.format(guido)) # hello guido

El problema de esto es: “¿Y cuál es la mejor? ¿Cada vez que salga una nueva forma debo refactorizar todo?”

En Ruby:

name = "matz "
puts "Hello, #{name}!"# hello matz 

Bonus: En Ruby puedo agregar expresiones en el string y son evaluadas, incluso se puede definir una clase.

puts "Hello world #{1 + 1}!"# hello world 2

Teniendo esas capacidades no es necesario crear más formas de formatear para permitir más features.

Métodos / Atributos privados

En Python no existen los métodos/atributos privados. Aun así la comunidad de Python (basado en lo que he visto del código de varias librerías) ha sentido la necesidad de tenerlos. Han optado por hacer uso del name mangling que ocurre cuando se definen variables/métodos con underscore para emular variables/métodos privados.

classPersona:def__init__(self, name, private_data):
        self.name = name
        self.__private_data = private_data
    
    def__private(self):#some operationreturnTruedefpublic_operation(self):
        self.__private()
        # more code  returnTrue

guido = Persona("guido", {'security_number': 123456})

guido.public_operation() # True

guido.__private() # AttributeError: 'Persona' object has no attribute '__private'
guido.__private_data # AttributeError: 'Persona' object has no attribute '__private_data'# Es complicado, pero aun se pueden acceder
guido._Persona__private() # True
guido._Persona__private_data # {'security_number': 123456}

Nota 1: en Ruby también se pueden acceder a métodos privados usando reflexion.
Nota de la nota: en Ruby en realidad los atributos no son privados, son protegidos.

La version de ruby de este código sería:

classPersonattr_accessor:namedefinitialize(name, private_data)
    @name = name
    @private_data = private_data
  enddefpublic_operation#some codeend

  private

  defprivate_operation#some codeend

La verdad es que esto hace que la legibilidad se pierda: si solo Python tuviera identificadores privados, podríamos evitar la sintaxis de los métodos mágicos y métodos/atributos privados.

Ejemplo: el código de mis sueños

classPersona:definit(name, private_data):        @name = name        @private_data = private_datadefpublic_operation():
        private()
        # more code  returnTrue

    private
    # methods below are privatedefprivate():#some operationreturnTrue

That’s it

Espero que les halla gustado. Si no están de acuerdo o creen que falto mencionar algo dejen sus comentarios y sigamos la conversación.

David
David
@davidtoca

13431Puntos

hace un año

Todas sus entradas
Escribe tu comentario
+ 2
Ordenar por:
3
110301Puntos

Interesante lo que mencionas del manejo de los strings, falta aclarar que la interpolación de strings es una característica nueva de python 3.6 y se llaman Literal Strings

print(f'hello {name}') # hello guido
0
2Puntos

El codigo de tus sueños se parece a Javascript 😃

0
678Puntos

Que buen articulo!. hace 10 años fue a amor a primera vista con Ruby, dado que me interesa mucho el tema de Data Science, siento la necesidad de aprender Python, pero me cuesta un mundo tomarle cariño!. igual sigo pensando que hoy dia uno debe conocer de ambos!

0
757Puntos

Muy buena comparacion. Siempre me llamo la atencion Ruby. Lo que nunca me quedo claro es si se usa solo para web, ¿O puede usarse para diversos tipos de aplicaciones como python?

En mi caso me acostumbre mucho al self y me parece muy comodo. Pero el tema de los string si que es verdad, en mi caso trato de usar siempre el .format() que me resulto mas comodo.

Leyendo mi comentario veo que termine optando por lo que me resulta mas comodo. No se si sea bueno o malo tener esa opcion de elegir…

Saludos.

0
267Puntos
un año

python 3.6 permite en el tema de strings la interpolación de este mod print(f"hola mundo{name}")

0
2235Puntos

interesante Ruby se ve más práctico en ciertos aspectos
Pasar de Python 2.5 a 2.7 o a 2.7.1 tampoco sería problema. Deberías leer un poco de semantic versions http://semver.org/