Aprendendo Python com Ruby; Diferenças e semelhanças

131

Eu conheço Ruby muito bem. Acredito que talvez eu precise aprender Python atualmente. Para quem conhece os dois, que conceitos são semelhantes entre os dois e quais são diferentes?

Estou procurando uma lista semelhante a uma cartilha que escrevi para Learning Lua for JavaScripters : coisas simples, como significado de espaço em branco e construções de loop; o nome de nilem Python e quais valores são considerados "verdadeiros"; é idiomática usar o equivalente a mape each, ou são Mumble somethingaboutlistcomprehensions Mumble a norma?

Se eu receber uma boa variedade de respostas, fico feliz em agregá-las em um wiki da comunidade. Ou então, todos vocês podem brigar e se abraçar para tentar criar a única lista abrangente e verdadeira.

Edit : Para ser claro, meu objetivo é o Python "adequado" e idiomático. Se houver um equivalente em Python inject, mas ninguém o usar, porque existe uma maneira melhor / diferente de atingir a funcionalidade comum de iterar uma lista e acumular um resultado ao longo do caminho, quero saber como você faz as coisas. Talvez eu atualize esta pergunta com uma lista de objetivos comuns, como você os alcança em Ruby e pergunte qual é o equivalente em Python.

Phrogz
fonte
1
a única coisa que eu li foi c2.com/cgi/wiki?PythonVsRuby , eu realmente não gosto de auto e os recuos, mas eu me acostumei com isso :)
Saif al Harthi
1
Relacionado: stackoverflow.com/questions/1113611/… (não tenho certeza se é uma duplicata, pois essa pergunta pede coisas sem equivalente).
19
@SilentGhost Eu discordo totalmente. Estou perguntando "O que é o mesmo entre os idiomas e o que é diferente?" Como mostrado por muitas das respostas abaixo, existem respostas muito claras e úteis possíveis para isso.
Phrogz 22/01
3
@Hrogz: eu vejo isso e torna a pergunta sem resposta.
SilentGhost
2
@ Phongong - Para repetir o que eu disse sobre o meta tópico que você postou, o problema com esta pergunta é que o espaço do problema é muito grande - é um tópico muito grande para apenas uma pergunta. Existem milhares de diferenças entre os dois idiomas.
Adam Davis

Respostas:

153

Aqui estão algumas diferenças importantes para mim:

  1. Ruby tem blocos; Python não.

  2. Python tem funções; Ruby não. No Python, você pode pegar qualquer função ou método e passá-lo para outra função. No Ruby, tudo é um método, e os métodos não podem ser passados ​​diretamente. Em vez disso, você precisa envolvê-los nos Proc para passá-los.

  3. Ruby e Python suportam fechamentos, mas de maneiras diferentes. No Python, você pode definir uma função dentro de outra função. A função interna possui acesso de leitura a variáveis ​​da função externa, mas não acesso de gravação. No Ruby, você define fechamentos usando blocos. Os fechamentos têm acesso completo de leitura e gravação a variáveis ​​do escopo externo.

  4. O Python possui compreensões de lista, que são bastante expressivas. Por exemplo, se você tiver uma lista de números, poderá escrever

    [x*x for x in values if x > 15]

    para obter uma nova lista dos quadrados de todos os valores maiores que 15. No Ruby, você teria que escrever o seguinte:

    values.select {|v| v > 15}.map {|v| v * v}

    O código Ruby não parece tão compacto. Também não é tão eficiente, pois primeiro converte a matriz de valores em uma matriz intermediária mais curta, contendo valores maiores que 15. Em seguida, ela pega a matriz intermediária e gera uma matriz final contendo os quadrados dos intermediários. A matriz intermediária é então descartada. Então, Ruby acaba com 3 matrizes na memória durante o cálculo; O Python precisa apenas da lista de entrada e da lista resultante.

    O Python também fornece compreensões de mapas semelhantes.

  5. Python suporta tuplas; Ruby não. No Ruby, você precisa usar matrizes para simular tuplas.

  6. Ruby suporta instruções switch / case; Python não.

  7. Ruby suporta o expr ? val1 : val2operador ternário padrão ; Python não.

  8. Ruby suporta apenas herança única. Se você precisar imitar várias heranças, poderá definir módulos e usar mix-ins para colocar os métodos do módulo em classes. O Python suporta herança múltipla em vez de mix-ins de módulo.

  9. O Python suporta apenas funções lambda de linha única. Blocos Ruby, que são um tipo de / tipo de funções lambda, podem ser arbitrariamente grandes. Por esse motivo, o código Ruby geralmente é escrito em um estilo mais funcional que o código Python. Por exemplo, para percorrer uma lista em Ruby, você normalmente faz

    collection.each do |value|
      ...
    end
    

    O bloco funciona como uma função que está sendo passada para collection.each. Se você fizesse a mesma coisa em Python, teria que definir uma função interna nomeada e depois passar para a coleção cada método (se a lista suportasse esse método):

    def some_operation(value):
      ...
    
    collection.each(some_operation)
    

    Isso não flui muito bem. Portanto, normalmente a seguinte abordagem não funcional seria usada no Python:

    for value in collection:
      ...
    
  10. Usar recursos de maneira segura é bem diferente entre os dois idiomas. Aqui, o problema é que você deseja alocar algum recurso (abrir um arquivo, obter um cursor no banco de dados, etc.), executar alguma operação arbitrária e, em seguida, fechá-lo de maneira segura, mesmo que ocorra uma exceção.

    No Ruby, como os blocos são muito fáceis de usar (consulte o item 9), você normalmente codifica esse padrão como um método que leva um bloco para a operação arbitrária executar no recurso.

    No Python, passar uma função para a ação arbitrária é um pouco mais complicado, já que você precisa escrever uma função interna nomeada (consulte # 9). Em vez disso, o Python usa uma withinstrução para manipulação segura de recursos. Consulte Como faço para limpar corretamente um objeto Python? para mais detalhes.

Clint Miller
fonte
2
3. O Python 3 nonlocalcorrige isso 4. O Python também fornece expressões geradoras (semelhantes às compreensões de lista, mas não calcule nada até que seja solicitado - pense nas compreensões de lista como expressões geradoras alimentadas list(que pega um iterável e retorna uma lista contendo tudo iterável produzido) - isso pode economizar muito esforço em alguns casos).
25
7. Sim, sim. val1 if expr else val2. 8. Embora eu o veja principalmente usado para aprimoramentos no estilo mixin.
2
@ClintMiller Whoa, nenhum interruptor / caso? Então, qual é a maneira sugerida de obter funcionalidade semelhante no Python? se / mais / se?
Phrogz 22/01
15
Seu exemplo de ruby ​​no nº 4 não é idiomático. Seria mais ruby-ish (e legível) escrever values.map{|v| v*v if v > 15}.compact. IMHO, isso é ainda mais expressivo (e certamente mais claro) do que o seu exemplo de python.
sml 27/01
10
Além do acima, use o! versão da função compacto evita uma cópia da matriz: values.map{|v| v*v if v > 15}.compact!. Isso significa que apenas a lista de entrada e a lista resultante existem na memória. Veja # 4 aqui: igvita.com/2008/07/08/6-optimization-tips-for-ruby-mri
sml
27

Passei alguns meses aprendendo Python após 6 anos de Ruby. Realmente não havia uma grande comparação entre os dois idiomas, então decidi criar e escrever um. Agora, ele se preocupa principalmente com a programação funcional, mas como você mencionou o injectmétodo Ruby , acho que estamos no mesmo comprimento de onda.

Espero que isso ajude: A 'feiúra' do Python

Alguns pontos que o levarão na direção certa:

  • Toda a utilidade de programação funcional que você usa no Ruby está no Python, e é ainda mais fácil. Por exemplo, você pode mapear as funções exatamente como você espera:

    def f(x):
        return x + 1
    
    map(f, [1, 2, 3]) # => [2, 3, 4]
    
  • Python não tem um método que atue como each. Como você usa apenas eachefeitos colaterais, o equivalente no Python é o loop for:

    for n in [1, 2, 3]:
        print n
    
  • A compreensão da lista é excelente quando: a) você precisa lidar com funções e coleções de objetos; eb) quando precisa iterar usando vários índices. Por exemplo, para encontrar todos os palíndromos em uma string (supondo que você tenha uma função p()que retorne true para palíndromos), tudo que você precisa é de uma única compreensão da lista:

    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.
fonte
3
Suspiro, li esse post e confirma minha suspeita: poucas pessoas entendem o papel e a utilidade de métodos especiais no Python. Eles são incrivelmente úteis e padronizados, e são sublinhados assim para evitar conflitos de nomes com os componentes internos que eles costumam implementar. Ninguém que realmente conhece Python está tentando desencorajar seu uso.
Rafe Kettler
5
Você parece não entender como os métodos funcionam. Um método é, essencialmente, uma função cujo primeiro argumento é uma instância da classe à qual o método pertence. Quando você escreve Class.method, o método é "independente" e o primeiro argumento deve ser uma Classinstância; quando você escreve object.method, o método é "vinculado" à objectinstância de Class. Isso permite que você escolha se deseja usar o mapa (etc) para chamar o método em uma instância diferente a cada vez (passar um método não acoplado) ou manter a instância fixa e passar um segundo argumento diferente a cada vez. Ambos são úteis.
LaC 23/01
2
Você está certo, eu não entendi como eles funcionavam. Desde a publicação do artigo, tenho uma noção melhor disso. Obrigado!
David J.
10
[s[x:y] for x in range(l) for y in range(x,l+1) if p(s[x:y])]- esta linha mostra como o Python é difícil de ler. Ao ler o código Ruby, você move os olhos da esquerda para a direita, sem retornos. Mas para ler o código Python, você precisa ir para a esquerda-direita-esquerda-direita-esquerda-direita ... e parênteses, parênteses, parênteses, parênteses ... Também no Python, você frequentemente precisa misturar métodos e funções. É uma loucura: E(C(A.B()).D())em vez de RubyA.B.C.D.E
Nakilon
2
@ Nakilon É por isso que você deve usar apenas a compreensão de listas aninhadas para casos realmente simples, e não como o descrito acima. Pode ser 'inteligente' escrever uma linha que encontre todos os palíndromos em uma string, mas é melhor reservar para o código de golfe. Para um código real que outra pessoa precise ler mais tarde, basta escrever algumas funções de linha. Então, sim, essa linha é difícil de ler, mas isso é culpa do programador, não da linguagem.
Cam Jackson
10

Minha sugestão: não tente aprender as diferenças. Aprenda a abordar o problema no Python. Assim como existe uma abordagem Ruby para cada problema (que funciona muito bem, fornecendo as limitações e os pontos fortes da linguagem), existe uma abordagem Python para o problema. ambos são diferentes. Para tirar o melhor proveito de cada idioma, você realmente deve aprender o idioma em si, e não apenas a "tradução" de um para o outro.

Agora, com isso dito, a diferença o ajudará a se adaptar mais rapidamente e a fazer 1 modificação em um programa Python. E isso é bom para começar a escrever. Mas tente aprender com outros projetos o por trás das decisões de arquitetura e design, e não o por trás da semântica da linguagem ...

ircmaxell
fonte
7
Agradeço sua sugestão. Concordo plenamente com o sentimento (que interpreto como "Aprenda a programar Python idiomático") . É exatamente isso que estou tentando fazer. Não estou perguntando "Qual é o nome do Python para o eachmétodo Ruby ?" Estou perguntando "Como as coisas são feitas corretamente em Python diferentes de Ruby e onde elas são feitas da mesma maneira?" Se o Python falseé realmente False, é tão importante saber como onde e quando devo fazer as coisas de uma maneira rubyesca, e onde e quando não devo.
Phrogz 22/01
2
@Phrogz: Isso é justo. A maneira como interpretei sua pergunta foi: Vamos fazer uma lista das diferenças para que possamos mudar a linguagem em que estamos programando . Mas é uma pergunta justa. Acho que interpretei mal o que você estava pedindo. Vou deixar isso aqui para referência, mas vai ser interessante ver o que mais vem à tona ...
ircmaxell
Estou aprendendo python e ruby ​​ao mesmo tempo e, no desenvolvedor de aplicativos da web, vejo mais semelhanças do que diferenças.
WesternGun 29/11
8

Conheço o pequeno Ruby, mas aqui estão alguns pontos sobre as coisas que você mencionou:

  • nil, o valor que indica a falta de um valor seria None(observe que você o verifica como se fosse , x is Noneou x is not Nonenão com ==- ou por coerção com o valor booleano, consulte o próximo ponto).
  • None, Números de zero-esque ( 0, 0.0, 0j(número complexo)) e coleções vazias ( [], {}, set(), a cadeia vazia "", etc.) são considerados truthy Falsas, tudo o resto é considerado.
  • Para efeitos colaterais, ( for-) faz um loop explicitamente. Para gerar um novo monte de coisas sem efeitos colaterais, use as compreensões de lista (ou seus parentes - expressões geradoras para iteradores preguiçosos de uma só vez, compreensões de ditado / conjunto para as coleções mencionadas).

Com relação ao loop: você possui for, que opera de forma iterável (! Sem contagem) e while, que faz o que você esperaria. O fromer é muito mais poderoso, graças ao amplo suporte para iteradores. Não apenas tudo o que pode ser um iterador, em vez de uma lista, é um iterador (pelo menos no Python 3 - no Python 2, você tem os dois e, infelizmente, o padrão é uma lista). Existem inúmeras ferramentas para trabalhar com iteradores - zipitera qualquer número de iteráveis ​​em paralelo, enumerateoferece (index, item)(em qualquer iterável, não apenas nas listas), até fatiando iteráveis ​​abruptos (possivelmente grandes ou infinitos)! Descobri que essas tarefas simplificam muitas e muitas outras tarefas em loop. Escusado será dizer que eles se integram perfeitamente à compreensão de listas, expressões geradoras, etc.


fonte
2
As expressões do gerador são legais. Eles oferecem ao Python um pouco dos recursos de avaliação preguiçosos de linguagens como Haskell.
Clint Miller
@Clint: Sim. E geradores completos são ainda mais capazes (embora não sejam necessários para casos simples, que são a maioria).
Por que você verifica com x is Noneou x is not None? Eu sempre verifico com x == Nonee x != None.
John
@ John: Se xdefine __eq__de uma maneira boba, poderia dar um falso positivo. Se o __eq__programa não for programado com o suficiente cuidado, poderá travar (por exemplo AttributeError) quando determinados valores (por exemplo None). Pelo contrário, isnão pode ser substituído - ele sempre compara a identidade do objeto, que é a maneira correta (mais robusta, mais simples e mais limpa) de verificar se há um singleton.
1
@John. "x é Nenhum" é a maneira absolutamente idiomática de fazer isso. python.net/~goodger/projects/pycon/2007/idiomatic/handout.html
tokland
6

No Ruby, variáveis ​​e métodos de instância não são totalmente relacionados, exceto quando você os relaciona explicitamente com o attr_accessor ou algo assim.

No Python, os métodos são apenas uma classe especial de atributo: uma que é executável.

Então, por exemplo:

>>> class foo:
...     x = 5
...     def y(): pass
... 
>>> f = foo()
>>> type(f.x)
<type 'int'>
>>> type(f.y)
<type 'instancemethod'>

Essa diferença tem muitas implicações, como, por exemplo, que se referir a fx se refere ao objeto de método, em vez de chamá-lo. Além disso, como você pode ver, fx é público por padrão, enquanto no Ruby, variáveis ​​de instância são privadas por padrão.

Paul Prescod
fonte
2
Na verdade, eu diria ainda mais: em Python, os métodos são apenas um tipo específico de atributo, enquanto no Ruby, os atributos são apenas um tipo específico de método. Algumas características contrastantes importantes entre as duas línguas cair fora desta: funções de primeira classe em Python, eo uniforme princípio de acesso em Ruby
philomory