Aprende todo un fin de semana sin pagar una suscripción 🔥

Regístrate

Comienza en:

17H

50M

19S

13

Lo que extraño de Ruby como desarrollador Python

18936Puntos

hace 6 años

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

18936Puntos

hace 6 años

Todas sus entradas
Escribe tu comentario
+ 2