Я очень хорошо знаю Руби. Я считаю, что мне, возможно, сейчас понадобится изучить Python. Для тех, кто знает и то, и другое: какие концепции между ними похожи, а какие отличаются?
Я ищу список, похожий на букварь, который я написал для Learning Lua for JavaScripters : простые вещи, такие как значение пробелов и конструкции цикла; имя nil
в Python и какие значения считаются «правдивыми»; это идиоматично использовать эквивалент map
и each
, или бормочут что-то о списках понимания бормочут нормой?
Если я получу множество ответов, я буду счастлив объединить их в вики сообщества. Или вы все можете драться и сплетничать друг с другом, чтобы попытаться составить единый полный список.
Изменить : Чтобы быть ясным, моя цель - «правильный» и идиоматический Python. Если есть Python-эквивалент inject
, но никто не использует его, потому что есть лучший / другой способ достичь общей функциональности - итерации списка и накопления результата по пути, я хочу знать, как вы это делаете. Возможно, я дополню этот вопрос списком общих целей, как их достичь в Ruby, и спрошу, какой эквивалент есть в Python.
Ответы:
Вот несколько ключевых отличий от меня:
В Ruby есть блоки; Python этого не делает.
У Python есть функции; Руби нет. В Python вы можете взять любую функцию или метод и передать их другой функции. В Ruby все является методом, и методы нельзя передавать напрямую. Вместо этого вы должны обернуть их в Proc, чтобы передать их.
И Ruby, и Python поддерживают замыкания, но по-разному. В Python вы можете определить функцию внутри другой функции. Внутренняя функция имеет доступ для чтения к переменным из внешней функции, но не для записи. В Ruby вы определяете замыкания с помощью блоков. Замыкания имеют полный доступ для чтения и записи к переменным из внешней области видимости.
В Python есть довольно выразительные списки. Например, если у вас есть список чисел, вы можете написать
чтобы получить новый список квадратов всех значений больше 15. В Ruby вам нужно будет написать следующее:
Код Ruby не кажется таким компактным. Это также не так эффективно, поскольку сначала он преобразует массив значений в более короткий промежуточный массив, содержащий значения больше 15. Затем он берет промежуточный массив и генерирует окончательный массив, содержащий квадраты промежуточных звеньев. Затем выкидывают промежуточный массив. Итак, во время вычислений в памяти Ruby остается 3 массива; Python нужен только список ввода и результирующий список.
Python также предоставляет аналогичные представления о картах.
Python поддерживает кортежи; Руби нет. В Ruby вы должны использовать массивы для имитации кортежей.
Ruby поддерживает операторы switch / case; Python этого не делает.
Ruby поддерживает стандартныйexpr ? val1 : val2
тернарный оператор; Python этого не делает.Ruby поддерживает только одиночное наследование. Если вам нужно имитировать множественное наследование, вы можете определять модули и использовать микшеры для втягивания методов модуля в классы. Python поддерживает множественное наследование, а не смешивание модулей.
Python поддерживает только однострочные лямбда-функции. Блоки Ruby, которые являются своего рода лямбда-функциями, могут быть сколь угодно большими. Из-за этого код Ruby обычно написан в более функциональном стиле, чем код Python. Например, чтобы перебрать список в Ruby, вы обычно делаете
Блок работает очень похоже на передаваемую функцию
collection.each
. Если бы вы сделали то же самое в Python, вам бы пришлось определить именованную внутреннюю функцию, а затем передать ее в коллекцию каждого метода (если список поддерживает этот метод):Это не очень красиво. Итак, как правило, в Python используется следующий нефункциональный подход:
Безопасное использование ресурсов на двух языках сильно различается. Здесь проблема в том, что вы хотите выделить некоторый ресурс (открыть файл, получить курсор базы данных и т. Д.), Выполнить над ним произвольную операцию, а затем закрыть его безопасным образом, даже если возникнет исключение.
В Ruby, поскольку блоки настолько просты в использовании (см. №9), вы обычно кодируете этот шаблон как метод, который принимает блок для произвольной операции, выполняемой над ресурсом.
В Python передача функции для произвольного действия немного сложнее, поскольку вам нужно написать именованную внутреннюю функцию (см. №9). Вместо этого Python использует
with
инструкцию для безопасной обработки ресурсов. См. Как правильно очистить объект Python? Больше подробностей.источник
nonlocal
исправляет это. 4. Python также дает вам выражения-генераторы (похожие на понимание списков, но не вычисляйте ничего, пока не попросите об этом - думайте о списках как о выражениях генератора,list
передаваемых в (который принимает итерацию и возвращает список, содержащий все итерация дала результат) - в некоторых случаях это может сэкономить много усилий).val1 if expr else val2
, 8. Хотя я вижу, что он в основном используется для увеличения в стиле миксинов.values.map{|v| v*v if v > 15}.compact
. ИМХО, это даже выразительнее (и, конечно, яснее), чем ваш пример на python.values.map{|v| v*v if v > 15}.compact!
. Это означает, что в памяти существуют только список ввода и список результатов. См. # 4 здесь: igvita.com/2008/07/08/6-optimization-tips-for-ruby-mriЯ только что потратил пару месяцев на изучение Python после 6 лет обучения Ruby. На самом деле не было большого сравнения для двух языков, поэтому я решил придумать и написать один сам. Теперь это в основном связано с функциональным программированием, но, поскольку вы упомянули Ruby's
inject
метод , я предполагаю, что мы на одной волне.Надеюсь, это поможет: «Уродство» Python
Пара моментов, которые помогут вам двигаться в правильном направлении:
Все возможности функционального программирования, которые вы используете в Ruby, заложены в Python, и это даже проще. Например, вы можете отображать функции точно так, как вы ожидаете:
У Python нет метода, который действует как
each
. Поскольку вы используете толькоeach
побочные эффекты, эквивалентом в Python является цикл for:Понимание списков великолепно, когда а) вам нужно иметь дело с функциями и коллекциями объектов вместе и б) когда вам нужно выполнить итерацию с использованием нескольких индексов. Например, чтобы найти все палиндромы в строке (при условии, что у вас есть функция,
p()
которая возвращает истину для палиндромов), все, что вам нужно, - это понимание одного списка:источник
Class.method
, метод является «несвязанным», и первым аргументом должен бытьClass
экземпляр; когда вы пишетеobject.method
, метод "привязан" кobject
экземпляруClass
. Это позволяет вам выбрать, использовать ли map (и т. Д.) Для вызова метода в экземпляре разницы каждый раз (передавать несвязанный метод) или сохранять экземпляр фиксированным и каждый раз передавать другой второй аргумент. Оба полезны.[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
Мое предложение: не пытайтесь изучать различия. Узнайте, как подойти к проблеме в Python. Точно так же, как есть подход Ruby к каждой проблеме (который очень хорошо работает с учетом ограничений и сильных сторон языка), есть подход Python к проблеме. они оба разные. Чтобы извлечь максимальную пользу из каждого языка, вам действительно следует изучить сам язык, а не просто «перевод» с одного на другой.
Теперь, с учетом сказанного, разница поможет вам быстрее адаптироваться и внести одно изменение в программу Python. И это нормально для начала писать. Но постарайтесь узнать из других проектов, почему архитектурные и дизайнерские решения лежат в основе решений, а не как, что стоит за семантикой языка ...
источник
each
метода Ruby ?» Я спрашиваю: «Чем все, что сделано правильно в Python, отличается от Ruby, и где они одинаковы правильно?» Если Pythonfalse
действительно существуетFalse
, это так же важно знать, как где и когда я должен делать что-то в стиле Rubyesque, а где и когда я не должен.Я знаю маленькую Руби, но вот несколько пунктов о том, что вы упомянули:
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)
(на любой итерации, а не только в списках), даже нарезку абритарных (возможно, больших или бесконечных) итераций! Я обнаружил, что это значительно упрощает многие задачи цикла. Излишне говорить, что они прекрасно интегрируются со списками, генераторами выражений и т. Д.источник
x is None
илиx is not None
? Я всегда проверяюx == None
иx != None
.x
определить__eq__
глупо, это может дать ложное срабатывание. Если__eq__
не запрограммировать достаточно тщательно, он можетAttributeError
дать сбой (например ) при задании определенных значений (напримерNone
). Напротив,is
его нельзя переопределить - он всегда сравнивает идентичность объекта, что является правильным (наиболее надежным, простым и чистым) способом проверки синглтона.В Ruby переменные экземпляра и методы полностью не связаны, за исключением случаев, когда вы явно связываете их с attr_accessor или чем-то подобным.
В Python методы - это просто особый класс атрибутов: исполняемый.
Так, например:
Это различие имеет множество последствий, например, ссылка на fx относится к объекту метода, а не к его вызову. Кроме того, как вы можете видеть, fx по умолчанию является общедоступным, тогда как в Ruby переменные экземпляра по умолчанию являются закрытыми.
источник