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 nil
em Python e quais valores são considerados "verdadeiros"; é idiomática usar o equivalente a map
e 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.
Respostas:
Aqui estão algumas diferenças importantes para mim:
Ruby tem blocos; Python não.
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.
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.
O Python possui compreensões de lista, que são bastante expressivas. Por exemplo, se você tiver uma lista de números, poderá escrever
para obter uma nova lista dos quadrados de todos os valores maiores que 15. No Ruby, você teria que escrever o seguinte:
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.
Python suporta tuplas; Ruby não. No Ruby, você precisa usar matrizes para simular tuplas.
Ruby suporta instruções switch / case; Python não.
Ruby suporta oexpr ? val1 : val2
operador ternário padrão ; Python não.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.
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
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):Isso não flui muito bem. Portanto, normalmente a seguinte abordagem não funcional seria usada no Python:
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
with
instrução para manipulação segura de recursos. Consulte Como faço para limpar corretamente um objeto Python? para mais detalhes.fonte
nonlocal
corrige 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 alimentadaslist
(que pega um iterável e retorna uma lista contendo tudo iterável produzido) - isso pode economizar muito esforço em alguns casos).val1 if expr else val2
. 8. Embora eu o veja principalmente usado para aprimoramentos no estilo mixin.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.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-mriPassei 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
inject
mé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:
Python não tem um método que atue como
each
. Como você usa apenaseach
efeitos colaterais, o equivalente no Python é o loop for: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:fonte
Class.method
, o método é "independente" e o primeiro argumento deve ser umaClass
instância; quando você escreveobject.method
, o método é "vinculado" àobject
instância deClass
. 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.[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
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 ...
fonte
each
mé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 Pythonfalse
é realmenteFalse
, é tão importante saber como onde e quando devo fazer as coisas de uma maneira rubyesca, e onde e quando não devo.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 seriaNone
(observe que você o verifica como se fosse ,x is None
oux is not None
nã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.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) ewhile
, 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 -zip
itera qualquer número de iteráveis em paralelo,enumerate
oferece(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
x is None
oux is not None
? Eu sempre verifico comx == None
ex != None
.x
define__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 exemploAttributeError
) quando determinados valores (por exemploNone
). Pelo contrário,is
nã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.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:
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.
fonte