Навчання Python від Ruby; Відмінності та схожість


131

Я дуже добре знаю Рубі. Я вважаю, що зараз мені може знадобитися вивчити Python. Для тих, хто знає обоє, які поняття схожі між ними, а які - різними?

Я шукаю список, схожий на буквар, який я написав для Learning Lua для JavaScripters : прості речі, такі як значення пробілу та петельні конструкції; ім'я nilв Python, і які значення вважаються "truthy"; чи ідіоматично використовувати еквівалент mapі each, або буркочуть щось про спільне розуміння , нарікають норму?

Якщо я отримаю різні відповіді, я радий об'єднати їх у вікі спільноти. Інакше ви всі можете битися і створювати шпаргалки один з одним, щоб спробувати створити єдиний справжній всебічний список.

Редагувати : Щоб було зрозуміло, моя мета - «правильний» та ідіоматичний Python. Якщо є еквівалент Python inject, але його ніхто не використовує, оскільки існує кращий / інший спосіб досягти загальної функціональності ітерації списку та накопичення результату по ходу, я хочу знати, як ви робите справи. Можливо, я оновлю це питання переліком загальних цілей, як ви їх досягаєте в Ruby, і запитаю, який еквівалент є в Python.


1
Єдине, що я читав, це c2.com/cgi/wiki?PythonVsRuby , я дійсно не люблю себе та відступи, але я звик до цього :)
Сайф аль Харті

1
Пов’язано: stackoverflow.com/questions/1113611/… (я не зовсім впевнений, чи це дублікат, оскільки це питання задає речі без еквівалента).

19
@SilentGhost Я абсолютно не згоден. Я запитую "Що між мовами однакове, а чим інше?" Як показано багатьма відповідями нижче, для цього можливі дуже чіткі та корисні відповіді.
Фрогз

3
@Phrogz: Я бачу це, і це питання не відповідає.
SilentGhost

2
@Phrongz - щоб повторити те, що я сказав на мета-темі, яку ви розмістили, проблема цього питання полягає в тому, що проблемний простір занадто великий - це занадто велика тема лише для одного питання. Існують тисячі відмінностей між двома мовами.
Адам Девіс

Відповіді:


153

Ось кілька ключових відмінностей для мене:

  1. У Рубі є блоки; Пітон ні.

  2. Python має функції; Рубі ні. У Python ви можете взяти будь-яку функцію чи метод і передати її іншій функції. У Ruby все є методом, і методи неможливо передати безпосередньо. Натомість вам потрібно загортати їх у програму Proc, щоб передавати їх.

  3. Ruby та Python підтримують закриття, але різними способами. У Python ви можете визначити функцію всередині іншої функції. Внутрішня функція має доступ до читання до змінних із зовнішньої функції, але не записує доступ. У Ruby ви визначаєте закриття за допомогою блоків. Закриття мають повний доступ для читання і запису до змінних із зовнішньої області.

  4. У Python є спільні розуміння, які досить виразні. Наприклад, якщо у вас є список номерів, ви можете написати

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

    щоб отримати новий перелік квадратів усіх значень більше 15. У Ruby вам потрібно буде написати наступне:

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

    Код Ruby не здається таким компактним. Він також не настільки ефективний, оскільки спочатку перетворює масив значень у коротший проміжний масив, що містить значення більше 15. Потім він приймає проміжний масив та формує остаточний масив, що містить квадрати проміжних. Потім проміжний масив викидається. Отже, Рубі закінчує 3 масиви пам'яті під час обчислення; Python потребує лише списку вводу та списку, що виходить.

    Python також забезпечує подібні розуміння карт.

  5. Python підтримує кортежі; Рубі ні. У Ruby ви повинні використовувати масиви для імітації кортежів.

  6. Ruby підтримує вимикачі / випадок випадку; Пітон ні.

  7. Ruby підтримує стандартного expr ? val1 : val2потрійного оператора; Пітон ні.

  8. Ruby підтримує лише одиночне успадкування. Якщо вам потрібно імітувати кілька успадкованих даних, ви можете визначити модулі та використовувати суміші, щоб перетягнути методи модулів у класи. Python підтримує багатократне успадкування, а не змішування модулів.

  9. Python підтримує лише однолінійні лямбда-функції. Рубінові блоки, які є ламбда-функціями, можуть бути довільно великими. Через це код Ruby зазвичай пишеться у більш функціональному стилі, ніж код Python. Наприклад, щоб перейти до списку в Ruby, ви зазвичай робите

    collection.each do |value|
      ...
    end
    

    Блок працює дуже схоже на функцію, яка передається collection.each. Якби ви робили те ж саме в Python, вам слід було б визначити іменовану внутрішню функцію, а потім передати її колекції кожному методу (якщо список підтримує цей метод):

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

    Це не дуже добре тече. Отже, зазвичай в Python використовується наступний нефункціональний підхід:

    for value in collection:
      ...
    
  10. Використання ресурсів у безпечний спосіб сильно відрізняється між двома мовами. Тут проблема полягає в тому, що ви хочете виділити якийсь ресурс (відкрити файл, отримати курсор бази даних тощо), виконати якусь довільну операцію над ним, а потім закрити її безпечним способом, навіть якщо трапляється виняток.

    У Ruby, оскільки блоки настільки прості у використанні (див. № 9), ти зазвичай кодуєш цей шаблон як метод, який бере блок для довільної операції, що виконується на ресурсі.

    У Python передача функції для довільної дії є дещо незграбною, оскільки вам потрібно написати внутрішню функцію (назва №9). Натомість Python використовує withоператор для безпечної роботи з ресурсами. Див. Як правильно очистити об’єкт Python? для отримання детальної інформації.


2
3. Python 3 nonlocalвиправляє це 4. Python також дає вам генераторні вирази (подібно до розуміння списків, але нічого не обчислюйте, поки не попросите - подумайте про розуміння списку як на генераторні вирази, що подаються list(що займає ітерабельність і повертає список, що містить усе ітерабельний вихід) - це може заощадити багато зусиль у деяких випадках).

25
7. Так, це так. val1 if expr else val2. 8. Хоча я бачу, що він використовується в основному для збільшення стилю міксин.

2
@ClintMiller Whoa, немає перемикача / корпусу? Отже, який запропонований спосіб досягти подібної функціональності в Python? якщо / ще / якщо?
Фрогз

15
Ваш рубіновий приклад у №4 не є ідіоматичним. Було б зручніше (і читабельніше) писати values.map{|v| v*v if v > 15}.compact. ІМХО, це ще виразніше (і, безумовно, зрозуміліше), ніж ваш приклад пітона.
sml

10
На додаток до сказаного, використовуючи! версія компактної параметр попереджує копію масиву: values.map{|v| v*v if v > 15}.compact!. Це означає, що в пам'яті існують лише список вхідних даних та отриманий список. Дивіться №4 тут: igvita.com/2008/07/08/6-optimization-tips-for-ruby-mri
sml

27

Я щойно провів пару місяців, вивчаючи Python після 6 років Ruby. Там справді не було великого порівняння для двох мов, тож я вирішив створити та написати сам. Зараз це стосується в основному функціонального програмування, але оскільки ви згадуєте injectметод Рубі , я здогадуюсь, що ми на одній довжині хвилі.

Я сподіваюся, що це допомагає: «Потворність» Пітона

Кілька пунктів, які дадуть вам рухатись у правильному напрямку:

  • Вся корисність функціонального програмування, яку ви використовуєте в Ruby, знаходиться в Python, і це ще простіше. Наприклад, ви можете картографувати функції точно так, як ви очікували:

    def f(x):
        return x + 1
    
    map(f, [1, 2, 3]) # => [2, 3, 4]
    
  • У Python не існує способу, який би діяв на зразок each. Оскільки ви використовуєте лише eachдля побічних ефектів, еквівалентом у Python є цикл for:

    for n in [1, 2, 3]:
        print n
    
  • Ознайомлення зі списком чудово, коли: а) вам потрібно спільно працювати з функціями та колекціями об'єктів; б) коли вам потрібно повторити, використовуючи кілька індексів. Наприклад, щоб знайти всі паліндроми в рядку (якщо припустити, що у вас є функція, p()яка повертає справжнє для паліндром), все, що вам потрібно, - це розуміння єдиного списку:

    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
Зітхаючи, я прочитав цю публікацію, і це підтверджує мою підозру: мало хто розуміє роль і корисність спеціальних методів у Python. Вони неймовірно корисні та стандартизовані, і вони підкреслюють так, щоб уникнути називання конфліктів із вбудованими, які вони часто реалізовують. Ніхто, хто насправді знає Python, не намагається перешкодити їх використанню.
Рейф Кеттлер

5
Ви, здається, не розумієте, як працюють методи. Метод, по суті, є функцією, першим аргументом якої є екземпляр класу, до якого належить метод. Коли ви пишете Class.method, метод є "незв'язаним", і перший аргумент повинен бути Classекземпляром; коли ви пишете object.method, метод "прив'язується" до objectекземпляра Class. Це дозволяє вибирати, чи використовувати карту (тощо) для виклику методу на різницькому екземплярі кожен раз (передавати незв'язаний метод), або тримати примірник фіксованим та передавати кожен другий аргумент кожен раз. Обидва корисні.
LaC

2
Ви маєте рацію, я не розумів, як вони працюють. З моменту публікації статті я отримав кращий сенс для неї. Дякую!
Девід Дж.

10
[s[x:y] for x in range(l) for y in range(x,l+1) if p(s[x:y])]- цей рядок показує, наскільки Python важкий для читання. Коли ви читаєте код Ruby, ви рухаєте очима зліва направо, без повернення. Але щоб прочитати код Python, вам потрібно перейти ліворуч-право-вліво-вправо-вліво-вправо ... і в дужки, в дужках, в дужках, в дужках ... Також в Python вам часто потрібно змішання методів і функцій. Це безумство: E(C(A.B()).D())замість РубіA.B.C.D.E
Накілон

2
@Nakilon Ось чому ви повинні використовувати вкладені списки лише для справді простих випадків, а не як вище. Це може бути "розумним", щоб написати однолінійку, яка знаходить усі паліндроми в рядку, але найкраще зарезервувати для гольфу з кодом. Для справжнього коду, який хтось ще повинен прочитати пізніше, ви б просто написали кілька функцій рядка. Так, так, цей рядок важко читати, але це вина програмиста, а не мова.
Cam Jackson

10

Моя пропозиція: не намагайтеся дізнатися відмінності. Дізнайтеся, як підійти до проблеми в Python. Так само, як існує підхід Ruby до кожної проблеми (який дуже добре дає обмеження та сильні сторони мови), існує Python підхід до проблеми. вони обидва різні. Щоб отримати найкраще з кожної мови, ви дійсно повинні вивчити саму мову, а не лише "переклад" з однієї на іншу.

Тепер, маючи на увазі, різниця допоможе вам швидше адаптуватися та внести 1 модифікацію в програму Python. І це добре для початку, щоб почати писати. Але спробуйте дізнатися з інших проектів, чому стоять архітектурні та дизайнерські рішення, а не те, як за семантикою мови ...


7
Я ціную вашу пропозицію. Я повністю погоджуюся з настроями (які я трактую як "Навчіться програмувати ідіоматичний Python") . Це саме те, що я намагаюся зробити. Я не запитую "Як називається Python для eachметоду Рубі ?" Я запитую "Як все зроблено належним чином на Python від Ruby, і де вони виконані правильно?" Якщо falseнасправді в Python False, це так само важливо знати, де і коли я повинен робити рубіскій спосіб, а де і коли я не повинен.
Фрогз

2
@Phrogz: Це справедливо. Я розтлумачив ваше запитання так: Давайте складемо список відмінностей, щоб ми могли просто змінити мову, на якій ми програмуємо . Але це справедливе питання. Напевно, я просто неправильно трактував те, що ви просили. Я залишу це для довідки, але буде цікаво подивитися, що ще виходить ...
ircmaxell

Я вивчаю пітон і рубін одночасно, і в програмі веб-додатків я бачу більше подібності, ніж різниці.
WesternGun

8

Я мало знаю Рубі, але ось декілька пунктів про речі, які ви згадали:

  • nil, значення, що вказує на відсутність значення, було б None(зауважте, що ви перевіряєте це як, x is Noneчи x is not Noneне ==- чи з примусу до булевого, див. наступний пункт).
  • NoneНуль-еск числа ( 0, 0.0, 0j(комплексне число)) і порожні колекції ( [], {}, set(), порожній рядок ""і т.д.) вважаються falsy, все інше вважається truthy.
  • Для побічних ефектів ( for-) петлю явно. Для створення нового набору речей без побічних ефектів використовуйте розуміння списку (або їх родичів - генераторні вирази для ледачих одноразових ітераторів, диктуйте / встановлюйте розуміння для цих колекцій).

Щодо циклічного циклу: у вас є for, що працює на ітерабельному (! Без підрахунку), і whileякий робить те, що ви очікували. Завдяки широкій підтримці ітераторів, відбірник набагато потужніший. Ітератор не лише майже все, що може бути ітератором, а не список - принаймні, у Python 3 - у Python 2, на жаль, у вас є обидва, за замовчуванням - це список). Численні інструменти для роботи з ітераторами - zipітералізує будь-яку кількість ітерабелів паралельно, enumerateдає вам (index, item)(на будь-якому ітерабельному, а не лише у списках), навіть нарізання атритаріїв (можливо, великих чи нескінченних) ітерабелів! Я виявив, що вони значно спрощують багато циклічних завдань. Зайве говорити, що вони добре поєднуються із розумінням списку, генераторними виразами тощо.


2
Вирази генератора класні. Вони надають Python трохи лінивих можливостей оцінювання таких мов, як Haskell.
Клінт Міллер

@Clint: Так. А повноцінні генератори ще більш здатні (хоча і не потрібні для простих випадків, яких, як правило, більшість).

Чому ви перевіряєте за допомогою x is Noneабо x is not None? Я завжди перевіряю з x == Noneі x != None.
Джон

@John: Якщо xвизначити __eq__нерозумно, це може дати помилковий позитив. Якщо програма __eq__не запрограмована досить обережно, вона може вийти з ладу (наприклад AttributeError) при заданих певних значеннях (тобто None). Навпаки, isїї не можна перекрити - вона завжди порівнює ідентичність об'єкта, що є правильним (найнадійнішим, найпростішим та найчистішим) способом перевірити наявність одинакових.

1
@John. "x є None" - це абсолютно ідіоматичний спосіб зробити це. python.net/~goodger/projects/pycon/2007/idiomatic/handout.html
tokland

6

У Ruby змінні екземплярів та методи абсолютно не пов'язані між собою, за винятком випадків, коли ви явно пов'язуєте їх з attr_accessor або чимось подібним.

У Python методи - це лише особливий клас атрибута: той, який виконується.

Так, наприклад:

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

Ця різниця має багато наслідків, як, наприклад, те, що посилання на fx стосується об'єкта методу, а не викликає його. Також, як ви бачите, fx є загальнодоступним, тоді як у Ruby змінні екземплярів за замовчуванням є приватними.


2
Насправді я б зазначив це ще більш різко: в Python методи є лише особливим атрибутом, тоді як у Ruby атрибути - це лише певний тип методу. З цього випадають деякі важливі контрастні особливості між двома мовами: Функції першого класу в Python та Принцип єдиного доступу в Ruby
філомонія
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.