Изучение Python из Ruby; Различия и сходства

131

Я очень хорошо знаю Руби. Я считаю, что мне, возможно, сейчас понадобится изучить Python. Для тех, кто знает и то, и другое: какие концепции между ними похожи, а какие отличаются?

Я ищу список, похожий на букварь, который я написал для Learning Lua for JavaScripters : простые вещи, такие как значение пробелов и конструкции цикла; имя nilв Python и какие значения считаются «правдивыми»; это идиоматично использовать эквивалент mapи each, или бормочут что-то о списках понимания бормочут нормой?

Если я получу множество ответов, я буду счастлив объединить их в вики сообщества. Или вы все можете драться и сплетничать друг с другом, чтобы попытаться составить единый полный список.

Изменить : Чтобы быть ясным, моя цель - «правильный» и идиоматический Python. Если есть Python-эквивалент inject, но никто не использует его, потому что есть лучший / другой способ достичь общей функциональности - итерации списка и накопления результата по пути, я хочу знать, как вы это делаете. Возможно, я дополню этот вопрос списком общих целей, как их достичь в Ruby, и спрошу, какой эквивалент есть в Python.

Phrogz
источник
1
единственное, что я прочитал, было c2.com/cgi/wiki?PythonVsRuby , я действительно не люблю себя и отступы, но я к этому привык :)
Саиф аль Харти
1
Связанный: stackoverflow.com/questions/1113611/… (я не совсем уверен, дубликат ли это, поскольку этот вопрос требует вещей без эквивалента).
19
@SilentGhost Я категорически не согласен. Я спрашиваю: «Что общего между языками, а что отличается?» Как показывают многие из приведенных ниже ответов, для этого есть очень четкие и полезные ответы.
Phrogz
3
@Phrogz: Я это понимаю, и поэтому на вопрос нет ответа.
SilentGhost
2
@Phrongz - Чтобы повторить то, что я сказал по мета-теме, которую вы опубликовали, проблема с этим вопросом в том, что проблемное пространство слишком велико - это слишком большая тема для только одного вопроса. Между этими двумя языками есть тысячи различий.
Адам Дэвис

Ответы:

153

Вот несколько ключевых отличий от меня:

  1. В Ruby есть блоки; Python этого не делает.

  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. Затем он берет промежуточный массив и генерирует окончательный массив, содержащий квадраты промежуточных звеньев. Затем выкидывают промежуточный массив. Итак, во время вычислений в памяти Ruby остается 3 массива; Python нужен только список ввода и результирующий список.

    Python также предоставляет аналогичные представления о картах.

  5. Python поддерживает кортежи; Руби нет. В Ruby вы должны использовать массивы для имитации кортежей.

  6. Ruby поддерживает операторы switch / case; Python этого не делает.

  7. Ruby поддерживает стандартный expr ? val1 : val2тернарный оператор; Python этого не делает.

  8. Ruby поддерживает только одиночное наследование. Если вам нужно имитировать множественное наследование, вы можете определять модули и использовать микшеры для втягивания методов модуля в классы. Python поддерживает множественное наследование, а не смешивание модулей.

  9. Python поддерживает только однострочные лямбда-функции. Блоки Ruby, которые являются своего рода лямбда-функциями, могут быть сколь угодно большими. Из-за этого код 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 Да нет переключателя / футляра? Итак, каков предлагаемый способ достижения аналогичной функциональности в Python? если / другое / , если?
Phrogz
15
Ваш пример с рубином в №4 не идиоматичен. Было бы более рубиново (и читабельно) писать values.map{|v| v*v if v > 15}.compact. ИМХО, это даже выразительнее (и, конечно, яснее), чем ваш пример на python.
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. На самом деле не было большого сравнения для двух языков, поэтому я решил придумать и написать один сам. Теперь это в основном связано с функциональным программированием, но, поскольку вы упомянули Ruby'sinject метод , я предполагаю, что мы на одной волне.

Надеюсь, это поможет: «Уродство» Python

Пара моментов, которые помогут вам двигаться в правильном направлении:

  • Все возможности функционального программирования, которые вы используете в 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, не пытается препятствовать их использованию.
Rafe Kettler
5
Вы, кажется, не понимаете, как работают методы. По сути, метод - это функция, первый аргумент которой является экземпляром класса, которому принадлежит метод. Когда вы пишете Class.method, метод является «несвязанным», и первым аргументом должен быть Classэкземпляр; когда вы пишете object.method, метод "привязан" к objectэкземпляру Class. Это позволяет вам выбрать, использовать ли map (и т. Д.) Для вызова метода в экземпляре разницы каждый раз (передавать несвязанный метод) или сохранять экземпляр фиксированным и каждый раз передавать другой второй аргумент. Оба полезны.
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())вместо Ruby'sA.B.C.D.E
Nakilon
2
@Nakilon Вот почему вы должны использовать вложенные списки только для действительно простых случаев, а не как указано выше. Было бы «разумно» написать однострочник, который находит все палиндромы в строке, но его лучше всего зарезервировать для кода гольфа. Для реального кода, который кто-то еще должен прочитать позже, вы должны просто написать несколько строковую функцию. Так что да, эту строку трудно прочитать, но это вина программиста, а не языка.
Кэм Джексон,
10

Мое предложение: не пытайтесь изучать различия. Узнайте, как подойти к проблеме в Python. Точно так же, как есть подход Ruby к каждой проблеме (который очень хорошо работает с учетом ограничений и сильных сторон языка), есть подход Python к проблеме. они оба разные. Чтобы извлечь максимальную пользу из каждого языка, вам действительно следует изучить сам язык, а не просто «перевод» с одного на другой.

Теперь, с учетом сказанного, разница поможет вам быстрее адаптироваться и внести одно изменение в программу Python. И это нормально для начала писать. Но постарайтесь узнать из других проектов, почему архитектурные и дизайнерские решения лежат в основе решений, а не как, что стоит за семантикой языка ...

ircmaxell
источник
7
Я ценю ваше предложение. Я полностью согласен с мнением (которое я интерпретирую как «научитесь программировать идиоматический Python») . Это именно то, что я пытаюсь сделать. Я не спрашиваю: «Как называется Python для eachметода Ruby ?» Я спрашиваю: «Чем все, что сделано правильно в Python, отличается от Ruby, и где они одинаковы правильно?» Если Python falseдействительно существует False, это так же важно знать, как где и когда я должен делать что-то в стиле Rubyesque, а где и когда я не должен.
Phrogz
2
@Phrogz: Это честно. Я интерпретировал ваш вопрос следующим образом: давайте составим список различий, чтобы мы могли просто изменить язык, на котором мы программируем . Но это справедливый вопрос. Думаю, я просто неверно истолковал то, о чем вы просили. Я оставлю это здесь для справки, но будет интересно посмотреть, что еще появится ...
ircmaxell
Я изучаю python и ruby ​​одновременно, и в разработке веб-приложений я вижу больше сходства, чем различий.
WesternGun
8

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

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

Относительно зацикливания: у вас есть for , который работает с итеративным (! Без подсчета), и whileкоторый делает то, что вы ожидаете. Фмер намного мощнее благодаря обширной поддержке итераторов. Не только почти все, что может быть итератором, а не списком, является итератором (по крайней мере, в Python 3 - в Python 2 у вас есть и то, и другое, и, к сожалению, по умолчанию это список). Существует множество инструментов для работы с итераторами - zipвыполняет итерацию любого количества итераций параллельно, enumerateдает вам (index, item)(на любой итерации, а не только в списках), даже нарезку абритарных (возможно, больших или бесконечных) итераций! Я обнаружил, что это значительно упрощает многие задачи цикла. Излишне говорить, что они прекрасно интегрируются со списками, генераторами выражений и т. Д.


источник
2
Генераторные выражения - это круто. Они дают Python немного возможностей ленивого оценивания таких языков, как Haskell.
Клинт Миллер
@ Клинт: Да. А полные генераторы еще более эффективны (хотя и не нужны для простых случаев, которых бывает большинство).
Почему вы проверяете с помощью x is Noneили x is not None? Я всегда проверяю x == Noneи x != None.
Джон
@John: Если xопределить __eq__глупо, это может дать ложное срабатывание. Если __eq__не запрограммировать достаточно тщательно, он может AttributeErrorдать сбой (например ) при задании определенных значений (например None). Напротив, isего нельзя переопределить - он всегда сравнивает идентичность объекта, что является правильным (наиболее надежным, простым и чистым) способом проверки синглтона.
1
@John. «x is 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
philomory