Belajar Python dari Ruby; Perbedaan dan Persamaan

131

Saya tahu Ruby dengan sangat baik. Saya percaya bahwa saya mungkin perlu mempelajari Python saat ini. Bagi mereka yang tahu keduanya, konsep apa yang sama antara keduanya, dan apa yang berbeda?

Saya mencari daftar yang mirip dengan primer yang saya tulis untuk Learning Lua for JavaScripters : hal-hal sederhana seperti signifikansi spasi putih dan konstruksi perulangan; nama nildalam Python, dan nilai apa yang dianggap "benar"; Apakah idiom untuk menggunakan yang setara dengan mapdan each, atau menggumamkan sesuatu tentang daftar pemahaman yang menggumamkan norma?

Jika saya mendapatkan beragam jawaban yang baik, saya senang menggabungkannya ke dalam wiki komunitas. Atau Anda semua dapat saling bertarung dan tidur untuk mencoba membuat daftar lengkap yang benar.

Sunting : Agar jelas, tujuan saya adalah "layak" dan idiom Python. Jika ada yang setara dengan Python inject, tetapi tidak ada yang menggunakannya karena ada cara yang lebih baik / berbeda untuk mencapai fungsi umum dari iterasi daftar dan mengumpulkan hasil di sepanjang jalan, saya ingin tahu bagaimana Anda melakukan sesuatu. Mungkin saya akan memperbarui pertanyaan ini dengan daftar tujuan umum, bagaimana Anda mencapainya di Ruby, dan bertanya apa yang setara di Python.

Phrogz
sumber
1
satu-satunya yang saya baca adalah c2.com/cgi/wiki?PythonVsRuby , saya benar-benar tidak suka diri dan lekukan tetapi saya terbiasa :)
Saif al Harthi
1
Terkait: stackoverflow.com/questions/1113611/… (Saya tidak yakin apakah itu duplikat, karena pertanyaan itu menanyakan hal-hal tanpa yang setara).
19
@ SilentGhost Saya sangat tidak setuju. Saya bertanya, "Apa yang sama antara bahasa-bahasa itu, dan apa yang berbeda?" Seperti yang ditunjukkan oleh banyak jawaban di bawah ini, ada jawaban yang sangat jelas dan bermanfaat yang memungkinkan untuk ini.
Phrogz
3
@ Phrogz: Saya melihat itu dan membuat pertanyaan tidak bisa dijawab.
SilentGhost
2
@ Philhongz - Untuk menggemakan apa yang saya katakan tentang topik meta yang Anda posting, masalah dengan pertanyaan ini adalah bahwa ruang masalahnya terlalu besar - terlalu besar untuk hanya satu pertanyaan. Ada ribuan perbedaan antara kedua bahasa.
Adam Davis

Jawaban:

153

Inilah beberapa perbedaan utama bagi saya:

  1. Ruby memiliki blok; Python tidak.

  2. Python memiliki fungsi; Ruby tidak. Dengan Python, Anda bisa mengambil fungsi atau metode apa saja dan meneruskannya ke fungsi lain. Di Ruby, semuanya adalah metode, dan metode tidak dapat diteruskan secara langsung. Sebagai gantinya, Anda harus membungkusnya dengan Proc untuk melewatinya.

  3. Ruby dan Python keduanya mendukung penutupan, tetapi dengan cara yang berbeda. Dengan Python, Anda bisa mendefinisikan fungsi di dalam fungsi lain. Fungsi dalam memiliki akses baca ke variabel dari fungsi luar, tetapi tidak akses tulis. Di Ruby, Anda menentukan penutupan menggunakan blok. Penutupan memiliki akses baca dan tulis penuh ke variabel dari lingkup luar.

  4. Python memiliki daftar pemahaman, yang cukup ekspresif. Misalnya, jika Anda memiliki daftar angka, Anda dapat menulis

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

    untuk mendapatkan daftar kuadrat baru dari semua nilai yang lebih besar dari 15. Di Ruby, Anda harus menulis yang berikut:

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

    Kode Ruby tidak terasa kompak. Ini juga tidak seefisien karena pertama-tama mengubah array nilai menjadi array menengah yang lebih pendek yang mengandung nilai lebih besar dari 15. Kemudian, dibutuhkan array perantara dan menghasilkan array akhir yang berisi kotak-kotak perantara. Array perantara kemudian dibuang. Jadi, Ruby berakhir dengan 3 array dalam memori selama perhitungan; Python hanya membutuhkan daftar input dan daftar yang dihasilkan.

    Python juga menyediakan pemahaman peta yang serupa.

  5. Python mendukung tuple; Ruby tidak. Di Ruby, Anda harus menggunakan array untuk mensimulasikan tupel.

  6. Ruby mendukung pernyataan switch / case; Python tidak.

  7. Ruby mendukung expr ? val1 : val2operator ternary standar ; Python tidak.

  8. Ruby hanya mendukung satu warisan. Jika Anda perlu meniru banyak pewarisan, Anda dapat mendefinisikan modul dan menggunakan campuran untuk menarik metode modul ke dalam kelas. Python mendukung multiple inheritance daripada module mix-in.

  9. Python hanya mendukung fungsi lambda satu baris. Blok Ruby, yang merupakan jenis / semacam fungsi lambda, bisa sangat besar. Karena itu, kode Ruby biasanya ditulis dalam gaya yang lebih fungsional daripada kode Python. Misalnya, untuk mengulang daftar di Ruby, biasanya Anda lakukan

    collection.each do |value|
      ...
    end

    Blok bekerja sangat mirip dengan fungsi yang diteruskan collection.each. Jika Anda melakukan hal yang sama dengan Python, Anda harus mendefinisikan fungsi dalam bernama dan kemudian meneruskannya ke kumpulan setiap metode (jika daftar mendukung metode ini):

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

    Itu tidak mengalir dengan sangat baik. Jadi, biasanya pendekatan non-fungsional berikut akan digunakan dalam Python:

    for value in collection:
      ...
  10. Menggunakan sumber daya dengan cara yang aman sangat berbeda antara kedua bahasa. Di sini, masalahnya adalah Anda ingin mengalokasikan beberapa sumber daya (membuka file, mendapatkan kursor database, dll), melakukan beberapa operasi sewenang-wenang di atasnya, dan kemudian menutupnya dengan cara yang aman bahkan jika pengecualian terjadi.

    Di Ruby, karena blok sangat mudah digunakan (lihat # 9), Anda biasanya akan mengkode pola ini sebagai metode yang mengambil blok untuk operasi sewenang-wenang untuk dilakukan pada sumber daya.

    Dalam Python, mengirimkan fungsi untuk tindakan arbitrer sedikit clunkier karena Anda harus menulis nama, fungsi bagian dalam (lihat # 9). Sebagai gantinya, Python menggunakan withpernyataan untuk penanganan sumber daya yang aman. Lihat Bagaimana cara membersihkan objek Python dengan benar? untuk lebih jelasnya.

Clint Miller
sumber
2
3. Python 3 nonlocalmemperbaiki ini 4. Python juga memberi Anda ekspresi generator (mirip dengan pemahaman daftar, tetapi jangan menghitung apa pun sampai diminta untuk - menganggap daftar pemahaman sebagai ekspresi generator yang diumpankan ke list(yang mengambil iterable dan mengembalikan daftar yang berisi segalanya) iterable yang dihasilkan) - ini dapat menghemat banyak usaha dalam beberapa kasus).
25
7. Ya itu. val1 if expr else val2. 8. Meskipun saya melihatnya sebagian besar digunakan untuk augmentasi gaya mixin.
2
@ClintMiller Whoa, tidak ada saklar / kasing? Jadi, apa cara yang disarankan untuk mencapai fungsi serupa di Python? jika / lain / jika?
Phrogz
15
Contoh ruby ​​Anda di # 4 tidak idiomatis. Akan lebih ruby-ish (dan mudah dibaca) untuk menulis values.map{|v| v*v if v > 15}.compact. IMHO, ini bahkan lebih ekspresif (dan tentunya lebih jelas) daripada contoh python Anda.
sml
10
Selain di atas, gunakan! versi fungsi kompak menghindari salinan dari array: values.map{|v| v*v if v > 15}.compact!. Ini berarti bahwa hanya daftar input dan daftar yang dihasilkan yang ada di memori. Lihat # 4 di sini: igvita.com/2008/07/08/6-optimisasi-tips-for-ruby-mri
sml
27

Saya baru saja menghabiskan beberapa bulan belajar Python setelah 6 tahun Ruby. Benar-benar tidak ada perbandingan yang bagus untuk kedua bahasa itu, jadi saya memutuskan untuk membuat dan menulis sendiri. Sekarang, ini terutama berkaitan dengan pemrograman fungsional, tetapi karena Anda menyebutkan injectmetode Ruby , saya kira kita berada pada gelombang yang sama.

Saya harap ini membantu: 'Keburukan' Python

Beberapa poin yang akan membuat Anda bergerak ke arah yang benar:

  • Semua kebaikan pemrograman fungsional yang Anda gunakan di Ruby adalah dalam Python, dan itu bahkan lebih mudah. Misalnya, Anda dapat memetakan fungsi persis seperti yang Anda harapkan:

    def f(x):
        return x + 1
    
    map(f, [1, 2, 3]) # => [2, 3, 4]
  • Python tidak memiliki metode yang bertingkah seperti each. Karena Anda hanya menggunakan eachuntuk efek samping, ekuivalen dalam Python adalah untuk loop:

    for n in [1, 2, 3]:
        print n
  • Pemahaman daftar sangat bagus ketika a) Anda harus berurusan dengan fungsi dan koleksi objek bersama-sama dan b) ketika Anda perlu beralih menggunakan beberapa indeks. Misalnya, untuk menemukan semua palindrom dalam string (dengan asumsi Anda memiliki fungsip() yang mengembalikan true untuk palindrom), yang Anda butuhkan hanyalah pemahaman daftar tunggal:

    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.
sumber
3
Sigh, saya membaca posting itu dan mengkonfirmasi kecurigaan saya: sedikit orang yang memahami peran dan kegunaan metode khusus dengan Python. Mereka sangat berguna dan terstandarisasi, dan mereka digarisbawahi seperti itu untuk menghindari konflik penamaan dengan builtin yang sering mereka terapkan. Tidak ada orang yang benar-benar tahu Python sedang mencoba untuk mencegah penggunaannya.
Rafe Kettler
5
Anda sepertinya tidak mengerti cara kerja metode. Suatu metode pada dasarnya adalah sebuah fungsi yang argumen pertamanya adalah turunan dari kelas yang dimiliki oleh metode tersebut. Saat Anda menulis Class.method, metode ini "tidak terikat" dan argumen pertama harus berupa Classinstance; ketika Anda menulis object.method, metode ini "terikat" ke objectinstance dari Class. Ini memungkinkan Anda memilih apakah akan menggunakan peta (dll) untuk memanggil metode pada instance perbedaan setiap kali (lulus metode tidak terikat), atau untuk menjaga instance tetap dan meneruskan argumen kedua yang berbeda setiap kali. Keduanya bermanfaat.
LaC
2
Anda benar, saya tidak mengerti bagaimana mereka bekerja. Sejak menerbitkan artikel itu, saya semakin mengerti. Terima kasih!
David J.
10
[s[x:y] for x in range(l) for y in range(x,l+1) if p(s[x:y])]- baris ini menunjukkan betapa sulitnya Python membaca. Ketika Anda membaca kode Ruby, Anda menggerakkan mata Anda dari kiri ke kanan, tanpa pengembalian. Tetapi untuk membaca kode Python, Anda harus ke kiri-kanan-kiri-kanan-kanan-kiri ... dan tanda kurung, tanda kurung, tanda kurung, tanda kurung ... Juga dalam Python Anda sering perlu mencampur metode dan fungsi. Kegilaan: E(C(A.B()).D())bukan RubyA.B.C.D.E
Nakilon
2
@Nakilon Itu sebabnya Anda hanya perlu menggunakan daftar nested list untuk kasus yang sangat sederhana, dan tidak seperti di atas. Mungkin 'pintar' untuk menulis satu-liner yang menemukan semua palindrom dalam sebuah string, tetapi sebaiknya disediakan untuk kode golf. Untuk kode nyata yang harus dibaca orang lain nanti, Anda cukup menulis beberapa fungsi baris. Jadi ya, kalimat itu sulit dibaca, tapi itu kesalahan programmer, bukan bahasa.
Cam Jackson
10

Saran saya: Jangan mencoba mempelajari perbedaannya. Pelajari cara mendekati masalah dengan Python. Sama seperti ada pendekatan Ruby untuk setiap masalah (yang bekerja dengan sangat baik memberikan keterbatasan dan kekuatan bahasa), ada pendekatan Python untuk masalah tersebut. keduanya berbeda. Untuk mendapatkan yang terbaik dari setiap bahasa, Anda harus benar-benar mempelajari bahasa itu sendiri, dan bukan hanya "terjemahan" dari satu ke yang lain.

Sekarang, dengan itu, perbedaannya akan membantu Anda beradaptasi lebih cepat dan membuat 1 dari modifikasi ke program Python. Dan itu bagus untuk memulai menulis. Tetapi cobalah belajar dari proyek lain mengapa di balik arsitektur dan keputusan desain daripada bagaimana di balik semantik bahasa ...

ircmaxell
sumber
7
Saya menghargai saran Anda. Saya sepenuhnya setuju dengan sentimen (yang saya artikan sebagai "Belajar memprogram Python idiomatik") . Itulah tepatnya yang saya coba lakukan. Saya tidak bertanya, "Apa nama Python untuk eachmetode Ruby ?" Saya bertanya, "Bagaimana hal dilakukan dengan benar di Python berbeda dari Ruby, dan di mana mereka dilakukan dengan benar sama?" Jika Python falsesebenarnya False, itu sama pentingnya untuk mengetahui di mana dan kapan aku harus melakukan sesuatu dengan cara Rubyesque, dan di mana dan kapan aku tidak boleh.
Phrogz
2
@ Phrogz: Itu adil. Cara saya menginterpretasikan pertanyaan Anda adalah: Mari kita membuat daftar perbedaan sehingga kita bisa mengubah bahasa tempat kita pemrograman . Tapi itu pertanyaan yang wajar. Saya kira saya salah menafsirkan apa yang Anda minta. Saya akan meninggalkan ini di sini untuk referensi, tetapi akan menarik untuk melihat apa lagi yang muncul ...
ircmaxell
Saya belajar python dan ruby ​​pada saat yang sama, dan di dev aplikasi web saya melihat lebih banyak kesamaan daripada perbedaan.
WesternGun
8

Saya tahu sedikit Ruby, tetapi di sini ada beberapa poin tentang hal-hal yang Anda sebutkan:

  • nil, nilai yang mengindikasikan kurangnya nilai, akan menjadi None(perhatikan bahwa Anda memeriksanya seperti x is Noneatau x is not None, tidak dengan ==- atau dengan paksaan ke boolean, lihat poin berikutnya).
  • None, Nomor nol-esque ( 0, 0.0, 0j(bilangan kompleks)) dan koleksi kosong ( [], {}, set(), string kosong"" , dll) dianggap falsy, segala sesuatu yang lain dianggap truthy.
  • Untuk efek samping, ( for-) loop secara eksplisit. Untuk menghasilkan banyak barang baru tanpa efek samping, gunakan daftar pemahaman (atau kerabat mereka - ekspresi generator untuk iterator satu kali malas, dikt / set pemahaman untuk koleksi tersebut).

Mengenai perulangan: Anda miliki for, yang beroperasi pada iterable (! No menghitung), dan while, yang melakukan apa yang Anda harapkan. Fromer jauh lebih kuat, berkat dukungan luas untuk iterator. Bukan hanya hampir semua yang bisa menjadi iterator, bukan daftar adalah iterator (setidaknya dalam Python 3 - dalam Python 2, Anda memiliki keduanya dan defaultnya adalah list, sayangnya). Ada banyak alat untuk bekerja dengan iterator - zipiterates sejumlah iterables secara paralel, enumeratememberi Anda (index, item)(pada setiap iterable, bukan hanya pada daftar), bahkan mengiris iterables iterables (mungkin besar atau tak terbatas)! Saya menemukan bahwa ini membuat banyak tugas pengulangan jauh lebih sederhana. Tidak perlu dikatakan, mereka mengintegrasikan dengan baik dengan daftar pemahaman, ekspresi generator, dll.


sumber
2
Ekspresi generator keren. Mereka memberi Python sedikit kemampuan evaluasi malas bahasa seperti Haskell.
Clint Miller
@Clint: Ya. Dan generator penuh bahkan lebih mampu (walaupun tidak diperlukan untuk kasus-kasus sederhana, yang merupakan mayoritas).
Mengapa Anda mengecek dengan x is Noneatau x is not None? Saya selalu mengecek dengan x == Nonedan x != None.
John
@ John: Jika xmendefinisikan __eq__dengan cara konyol, itu bisa memberikan positif palsu. Jika __eq__tidak diprogram dengan cukup hati-hati, itu bisa macet (misalnya AttributeError) ketika diberi nilai-nilai tertentu (yaitu None). Sebaliknya, istidak dapat ditimpa - selalu membandingkan identitas objek, yang merupakan cara yang tepat (paling kuat, paling sederhana dan paling bersih) untuk memeriksa singleton.
1
@ John. "x is None" adalah cara yang benar-benar idiomatis untuk melakukannya. python.net/~goodger/projects/pycon/2007/idiomatic/handout.html
tokland
6

Di Ruby, variabel instan dan metode sama sekali tidak terkait, kecuali ketika Anda secara eksplisit menghubungkannya dengan attr_accessor atau sesuatu seperti itu.

Dalam Python, metode hanyalah kelas atribut khusus: satu yang dapat dieksekusi.

Jadi misalnya:

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

Perbedaan itu memiliki banyak implikasi, seperti misalnya yang merujuk ke fx merujuk pada objek metode, daripada memanggilnya. Juga, seperti yang Anda lihat, fx bersifat publik secara default, sedangkan di Ruby, variabel instan bersifat pribadi secara default.

Paul Prescod
sumber
2
Sebenarnya, saya akan menyatakannya dengan lebih jelas: dengan Python, metode hanyalah jenis atribut tertentu, sedangkan di Ruby, atribut hanyalah jenis metode tertentu. Beberapa fitur kontras yang penting antara kedua bahasa tidak termasuk di sini: Fungsi Kelas Pertama dengan Python, dan Prinsip Akses Seragam di Ruby
philomory