Imparare Python da Ruby; Differenze e somiglianze


131

Conosco molto bene Ruby. Credo che potrei aver bisogno di imparare Python al momento. Per coloro che conoscono entrambi, quali concetti sono simili tra i due e quali sono diversi?

Sto cercando un elenco simile a un primer che ho scritto per Learning Lua per JavaScripters : cose semplici come il significato degli spazi bianchi e costrutti in loop; il nome di nilin Python e quali valori sono considerati "veritieri"; è idiomatico usare l'equivalente di mape each, o sono mumble qualcosa che le comprensioni dell'elenco mummano la norma?

Se ricevo una buona varietà di risposte, sono felice di aggregarle in un wiki della community. Oppure tutti voi potete combattere e crogiolarvi l'un l'altro per cercare di creare un vero elenco completo.

Modifica : Per essere chiari, il mio obiettivo è "vero" e idiomatico Python. Se esiste un equivalente di Python inject, ma nessuno lo usa perché esiste un modo migliore / diverso per ottenere la funzionalità comune di iterare un elenco e accumulare un risultato lungo la strada, voglio sapere come fai le cose. Forse aggiornerò questa domanda con un elenco di obiettivi comuni, come li raggiungi in Ruby e chiederò quale sia l'equivalente in Python.


1
l'unica cosa che ho letto è stata c2.com/cgi/wiki?PythonVsRuby , non mi piacciono molto le me stesso e le indentazioni ma mi sono abituato :)
Saif al Harthi,

1
Correlati: stackoverflow.com/questions/1113611/… (Non sono del tutto sicuro se si tratta di un duplicato, poiché quella domanda richiede cose senza un equivalente).

19
@SilentGhost Non sono assolutamente d'accordo. Sto chiedendo "Qual è lo stesso tra le lingue e che cosa è diverso?" Come indicato da molte delle risposte di seguito, ci sono risposte molto chiare e utili per questo.
Phrogz,

3
@Phrogz: lo vedo e rende la domanda irrisolvibile.
SilentGhost,

2
@Phrongz - Per riecheggiare quello che ho detto sul meta argomento che hai pubblicato, il problema con questa domanda è che lo spazio del problema è troppo grande - è un argomento troppo grande per una sola domanda. Ci sono migliaia di differenze tra le due lingue.
Adam Davis,

Risposte:


153

Ecco alcune differenze chiave per me:

  1. Ruby ha i blocchi; Python no.

  2. Python ha funzioni; Ruby no. In Python, puoi prendere qualsiasi funzione o metodo e passarlo a un'altra funzione. In Ruby, tutto è un metodo e i metodi non possono essere passati direttamente. Invece, devi avvolgerli in Proc per passarli.

  3. Ruby e Python supportano entrambe le chiusure, ma in diversi modi. In Python, puoi definire una funzione all'interno di un'altra funzione. La funzione interna ha accesso in lettura alle variabili dalla funzione esterna, ma non accesso in scrittura. In Ruby, definisci le chiusure usando i blocchi. Le chiusure hanno pieno accesso in lettura e scrittura alle variabili dall'ambito esterno.

  4. Python ha una comprensione delle liste, che sono piuttosto espressive. Ad esempio, se si dispone di un elenco di numeri, è possibile scrivere

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

    per ottenere un nuovo elenco dei quadrati di tutti i valori maggiori di 15. In Ruby, dovresti scrivere quanto segue:

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

    Il codice Ruby non sembra compatto. Inoltre, non è altrettanto efficiente poiché converte prima l'array di valori in un array intermedio più corto contenente i valori maggiori di 15. Quindi, prende l'array intermedio e genera un array finale contenente i quadrati degli intermedi. L'array intermedio viene quindi eliminato. Quindi, Ruby finisce con 3 array in memoria durante il calcolo; Python necessita solo dell'elenco di input e dell'elenco risultante.

    Python fornisce anche una comprensione simile della mappa.

  5. Python supporta le tuple; Ruby no. In Ruby, devi usare le matrici per simulare le tuple.

  6. Ruby supporta le istruzioni switch / case; Python no.

  7. Ruby supporta l' expr ? val1 : val2operatore ternario standard ; Python no.

  8. Ruby supporta solo una singola eredità. Se è necessario imitare l'ereditarietà multipla, è possibile definire i moduli e utilizzare i mix-in per inserire i metodi del modulo in classi. Python supporta l'ereditarietà multipla anziché i mix-in dei moduli.

  9. Python supporta solo funzioni lambda a linea singola. I blocchi ruby, che sono una sorta di / sorta di funzioni lambda, possono essere arbitrariamente grandi. Per questo motivo, il codice Ruby è in genere scritto in uno stile più funzionale rispetto al codice Python. Ad esempio, per scorrere su un elenco in Ruby, in genere lo fai

    collection.each do |value|
      ...
    end

    Il blocco funziona in modo molto simile a una funzione passata a collection.each. Se dovessi fare la stessa cosa in Python, dovresti definire una funzione interna denominata e poi passarla alla raccolta ogni metodo (se l'elenco supporta questo metodo):

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

    Non scorre molto bene. Quindi, in genere in Python verrebbe utilizzato il seguente approccio non funzionale:

    for value in collection:
      ...
  10. L'uso delle risorse in modo sicuro è abbastanza diverso tra le due lingue. Qui, il problema è che si desidera allocare alcune risorse (aprire un file, ottenere un cursore di database, ecc.), Eseguire alcune operazioni arbitrarie su di esso e quindi chiuderlo in modo sicuro anche se si verifica un'eccezione.

    In Ruby, poiché i blocchi sono così facili da usare (vedi # 9), in genere codifichi questo modello come un metodo che accetta un blocco per eseguire l'operazione arbitraria sulla risorsa.

    In Python, passare una funzione per l'azione arbitraria è un po 'più clunkier poiché devi scrivere una funzione interna denominata (vedi # 9). Invece, Python utilizza withun'istruzione per la gestione sicura delle risorse. Vedi Come posso pulire correttamente un oggetto Python? per ulteriori dettagli.


2
3. Python 3 nonlocalrisolve questo problema 4. Python ti dà anche espressioni del generatore (simili alle comprensioni dell'elenco, ma non calcola nulla fino a quando non ti viene chiesto - pensa alle comprensioni dell'elenco come espressioni del generatore alimentate list(che prende un iterabile e restituisce un elenco che contiene tutto l'iterabile ceduto) - questo può risparmiare molto sforzo in alcuni casi).

25
7. Sì, lo fa. val1 if expr else val2. 8. Anche se lo vedo principalmente usato per l'aumento in stile mixin.

2
@ClintMiller Whoa, nessun interruttore / caso? Quindi, qual è il modo suggerito per ottenere funzionalità simili in Python? if / else / if?
Phrogz,

15
Il tuo esempio di rubino in # 4 non è idiomatico. Scrivere sarebbe più rubino (e leggibile) values.map{|v| v*v if v > 15}.compact. IMHO, questo è ancora più espressivo (e sicuramente più chiaro) del tuo esempio di Python.
SML

10
Oltre a quanto sopra, usando! versione della funzione compatto evita una copia dell'array: values.map{|v| v*v if v > 15}.compact!. Ciò significa che nella memoria esistono solo l'elenco di input e l'elenco risultante. Vedi # 4 qui: igvita.com/2008/07/08/6-optimization-tips-for-ruby-mri
sml

27

Ho appena trascorso un paio di mesi ad imparare Python dopo 6 anni di Ruby. Non c'è stato davvero un grande confronto là fuori per le due lingue, quindi ho deciso di inventarmi e scriverne uno io. Ora, si occupa principalmente di programmazione funzionale, ma dal momento che menzioni il injectmetodo di Ruby , immagino che siamo sulla stessa lunghezza d'onda.

Spero che questo aiuti: la "bruttezza" di Python

Un paio di punti che ti faranno muovere nella giusta direzione:

  • Tutta la bontà della programmazione funzionale che usi in Ruby è in Python ed è ancora più semplice. Ad esempio, puoi mappare le funzioni esattamente come ti aspetteresti:

    def f(x):
        return x + 1
    
    map(f, [1, 2, 3]) # => [2, 3, 4]
  • Python non ha un metodo che si comporta come each. Dato che usi solo eachper gli effetti collaterali, l'equivalente in Python è il ciclo for:

    for n in [1, 2, 3]:
        print n
  • La comprensione dell'elenco è ottima quando a) devi gestire insieme funzioni e raccolte di oggetti eb) quando devi iterare usando più indici. Ad esempio, per trovare tutti i palindromi in una stringa (supponendo che tu abbia una funzione p()che ritorna vera per i palindromi), tutto ciò di cui hai bisogno è una singola lista di comprensione:

    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])]

3
Sospiro, ho letto quel post e questo conferma il mio sospetto: poche persone capiscono il ruolo e l'utilità dei metodi speciali in Python. Sono incredibilmente utili e standardizzati e sottolineati in questo modo per evitare conflitti di denominazione con i builtin che spesso implementano. Nessuno che sappia effettivamente Python sta cercando di scoraggiare il loro uso.
Rafe Kettler,

5
Sembra che tu non capisca come funzionano i metodi. Un metodo è essenzialmente una funzione il cui primo argomento è un'istanza della classe a cui appartiene il metodo. Quando scrivi Class.method, il metodo è "non associato" e il primo argomento dovrebbe essere Classun'istanza; quando si scrive object.method, il metodo è "associato" objectall'istanza di Class. Ciò consente di scegliere se utilizzare la mappa (ecc.) Per chiamare il metodo su un'istanza di differenza ogni volta (passare un metodo non associato) o mantenere l'istanza fissa e passare un secondo argomento diverso ogni volta. Entrambi sono utili.
LaC,

2
Hai ragione, non ho capito come funzionavano. Da quando ho pubblicato l'articolo, ho capito meglio. Grazie!
David J.

10
[s[x:y] for x in range(l) for y in range(x,l+1) if p(s[x:y])]- questa riga mostra quanto sia difficile Python per la lettura. Quando leggi il codice Ruby muovi gli occhi da sinistra a destra, senza ritorni. Ma per leggere il codice Python, devi andare da sinistra-destra-sinistra-destra-sinistra-destra ... e parentesi, parentesi, parentesi, parentesi ... Anche in Python hai spesso bisogno di mescolare metodi e funzioni. È una follia: E(C(A.B()).D())invece di Ruby'sA.B.C.D.E
Nakilon,

2
@Nakilon Ecco perché dovresti usare la comprensione dell'elenco nidificato solo per casi davvero semplici e non come sopra. Potrebbe essere "intelligente" scrivere un one-liner che trova tutti i palindromi in una stringa, ma è meglio riservato al codice golf. Per il vero codice che qualcun altro deve leggere in seguito, dovresti semplicemente scrivere alcune funzioni di linea. Quindi sì, quella riga è difficile da leggere, ma è colpa del programmatore, non della lingua.
Cam Jackson,

10

Il mio consiglio: non cercare di imparare le differenze. Scopri come affrontare il problema in Python. Proprio come c'è un approccio Ruby per ogni problema (che funziona molto bene dando i limiti e i punti di forza del linguaggio), c'è un approccio Python al problema. sono entrambi diversi. Per ottenere il meglio da ogni lingua, dovresti davvero imparare la lingua stessa e non solo la "traduzione" dall'una all'altra.

Ora, detto questo, la differenza ti aiuterà ad adattarti più velocemente e apportare 1 modifica a un programma Python. E va bene per cominciare a scrivere. Ma prova ad imparare da altri progetti il ​​perché dietro le decisioni di architettura e design piuttosto che il come dietro la semantica del linguaggio ...


7
Apprezzo il tuo suggerimento. Concordo pienamente con il sentimento (che interpreto come "Impara a programmare Python idiomatico") . Questo è esattamente quello che sto cercando di fare. Non sto chiedendo "Qual è il nome Python per il eachmetodo di Ruby ?" Sto chiedendo "In che modo le cose fatte correttamente in Python sono diverse da quelle di Ruby, e dove sono fatte correttamente le stesse?" Se Python falseè in realtà False, è importante sapere come e dove dovrei fare le cose in modo rubyesque, e dove e quando non dovrei.
Phrogz,

2
@Phrogz: È giusto. Il modo in cui ho interpretato la tua domanda è stato: facciamo un elenco delle differenze in modo da poter semplicemente cambiare la lingua in cui stiamo programmando . Ma è una domanda giusta. Immagino di aver male interpretato ciò che stavi chiedendo. Lascio questo qui come riferimento, ma sarà interessante vedere cos'altro viene fuori ...
Ircmaxell,

Sto imparando python e ruby ​​allo stesso tempo, e negli sviluppatori di app web vedo più somiglianze che differenze.
WesternGun

8

Conosco il piccolo Ruby, ma qui ci sono alcuni punti elenco sulle cose che hai menzionato:

  • nil, il valore che indica la mancanza di un valore sarebbe None(nota che lo controlli come x is Noneo x is not None, non con ==- o dalla coercizione a booleana, vedi il punto successivo).
  • None, Numeri zero esque ( 0, 0.0, 0j(numero complesso)) e raccolte vuoti ( [], {}, set(), la stringa vuota "", ecc) sono considerati falsy, tutto il resto è considerato truthy.
  • Per gli effetti collaterali, il forciclo ( -) esplicitamente. Per generare un nuovo gruppo di cose senza effetti collaterali, usa la comprensione delle liste (o i loro parenti - espressioni generatrici per gli iteratori pigri di una volta, comprendi le dettature / imposta per le suddette raccolte).

Per quanto riguarda il looping: sì for, che opera in modo iterabile (! Senza contare) e whileche fa ciò che ti aspetteresti. Il fromer è molto più potente, grazie al vasto supporto per gli iteratori. Non solo quasi tutto ciò che può essere un iteratore invece di un elenco è un iteratore (almeno in Python 3 - in Python 2, hai entrambi e il default è un elenco, purtroppo). Sono numerosi gli strumenti per lavorare con gli iteratori: zipitera in parallelo un numero qualsiasi di iterabili, enumerateti dà (index, item)(su qualsiasi iterabile, non solo su elenchi), anche tagliando iterabili abrasivi (possibilmente grandi o infiniti)! Ho scoperto che questi rendono molte più attività di looping molto più semplici. Inutile dire che si integrano perfettamente con la comprensione dell'elenco, le espressioni del generatore, ecc.


2
Le espressioni del generatore sono fantastiche. Offrono a Python un po 'delle pigre capacità di valutazione di linguaggi come Haskell.
Clint Miller,

@Clint: Sì. E i generatori completi sono ancora più capaci (anche se non necessari per casi semplici, che sembrano essere la maggioranza).

Perché controlli con x is Noneo x is not None? Controllo sempre con x == Nonee x != None.
John,

@Giovanni: se xdefinito __eq__in modo sciocco, potrebbe dare un falso positivo. Se __eq__non è programmato con sufficiente attenzione, potrebbe arrestarsi in modo anomalo (ad es. AttributeError) Quando vengono forniti determinati valori (ad es None.). Al contrario, isnon può essere ignorato: confronta sempre l'identità dell'oggetto, che è il modo giusto (più robusto, più semplice e più pulito) per verificare la presenza di un singleton.

1
@John. "x is None" è il modo assolutamente idiomatico per farlo. python.net/~goodger/projects/pycon/2007/idiomatic/handout.html
tokland

6

In Ruby, le variabili e i metodi di istanza sono completamente indipendenti, tranne quando li si collega esplicitamente con attr_accessor o qualcosa del genere.

In Python, i metodi sono solo una classe speciale di attributo: uno che è eseguibile.

Quindi per esempio:

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

Questa differenza ha molte implicazioni, come ad esempio il fatto che fare riferimento a FX si riferisce all'oggetto metodo, piuttosto che chiamarlo. Inoltre, come puoi vedere, fx è pubblico per impostazione predefinita, mentre in Ruby, le variabili di istanza sono private per impostazione predefinita.


2
In realtà, lo direi ancora più nettamente: in Python, i metodi sono solo un particolare tipo di attributo, mentre in Ruby gli attributi sono solo un particolare tipo di metodo.
Ne derivano
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.