Является ли `import module` лучшим стилем кодирования, чем` from module import function`? - PullRequest
66 голосов
/ 16 ноября 2009

Пусть from module import function будет называться стилем кодирования FMIF.

Пусть import module будет называться стилем кодирования IM.

Пусть from package import module будет называться стилем кодирования FPIM.

Почему IM + FPIM считается лучшим стилем кодирования, чем FMIF? (См. этот пост для вдохновения для этого вопроса.)

Вот некоторые критерии, которые побуждают меня предпочесть FMIF, а не IM:

  1. Недостаток кода: он позволяет мне использовать более короткие имена функций и, таким образом, помогает придерживаться соглашения о 80 столбцах на строку.
  2. Удобочитаемость: chisquare(...) выглядит более читабельным, чем scipy.stats.stats.chisquare(...). Хотя это субъективный критерий, я думаю, что большинство людей согласятся.
  3. Простота перенаправления: если я использую FMIF и по какой-то причине через некоторое время хочу перенаправить python для определения function из alt_module вместо module, мне нужно изменить только одну строку: from alt_module import function. Если бы я использовал IM, мне нужно было бы изменить много строк кода.
Я понимаю, что FPIM каким-то образом сводит на нет первые две проблемы, но как насчет третьей?

Меня интересуют все причины, по которым IM + FPIM может быть лучше, чем FMIF, но, в частности, мне было бы интересно разработать следующие пункты , упомянутые здесь :

Плюсы для IM:

  1. легкость насмешек / инъекций в тестах. (Я не очень знаком с насмешками, хотя недавно узнал, что означает этот термин. Можете ли вы показать код, который демонстрирует, насколько IM лучше, чем FMIF, здесь?)
  2. возможность гибкого изменения модуля путем переопределения некоторых записей. (Должно быть, я что-то неправильно понимаю, потому что это, кажется, преимущество FMIF перед IM. См. Мою третью причину в пользу FMIF выше.)
  3. предсказуемое и контролируемое поведение при сериализации и восстановлении ваших данных. (Я действительно не понимаю, как выбор IM или FMIF влияет на эту проблему. Пожалуйста, уточните.)
  4. Я понимаю, что FMIF "загрязняет мое пространство имен", но помимо того, что это звучит негативно, я не понимаю, как это повредит коду каким-либо конкретным образом.
PS. При написании этого вопроса я получил предупреждение, что вопрос кажется субъективным и, вероятно, будет закрыт. Пожалуйста, не закрывайте это. Я не ищу субъективное мнение, а скорее конкретные ситуации кодирования, где IM + FPIM явно лучше, чем FMIF.

Большое спасибо.

Ответы [ 5 ]

44 голосов
/ 16 ноября 2009

Список негативов, которые вы перечисляете для IM / FPIM, часто можно улучшить, если правильно использовать предложение as. from some.package import mymodulewithalongname as mymod может с пользой сократить ваш код и повысить его читабельность, а если завтра вы переименуете mymodulewithalongname в somethingcompletelydifferent, предложение as можно будет использовать как один оператор для редактирования.

Считайте, что ваш pro-FMIF пункт 3 (назовите его R для перенаправления) против вашего pro-FPIM пункта 2 (назовите его F для гибкости): R означает облегчение потери целостности границ модуля, в то время как F укрепляет его. Несколько функций, классов и переменных в модуле часто предназначены для совместной работы: они не должны независимо переключаться на разные значения. Например, рассмотрим модуль random и его функции seed и uniform: если вы переключите импорт только одного из них в другой модуль, то вы нарушите обычное соединение между вызовами на seed и результаты звонков на uniform. Когда модуль хорошо спроектирован, с целостностью и целостностью, облегчение R ломать границы модуля на самом деле отрицательно - это облегчает делать то, что вам лучше, не делать.

И наоборот, F - это то, что позволяет координировать переключение связанных функций, классов и переменных (то есть, как правило, сущностей, которые принадлежат вместе, по модульности). Например, чтобы сделать тестирование повторяемым (FPIM pro-point 1), вы смоделируете и seed и random в модуле random, и если ваш код следует за FPIM, у вас все настроено, координация гарантирована; но если у вас есть код, который импортировал функции напрямую, вы должны выследить каждый такой модуль и повторять макеты снова и снова. Для того, чтобы тесты были полностью повторяемыми, обычно также требуется «скоординированное моделирование» функций даты и времени - если вы используете from datetime import datetime в некоторых модулях, вам нужно найти и смоделировать их все (а также всех, кто делает from time import time и т. Д. ) чтобы убедиться, что все время получено, когда различные части системы спрашивают "так сколько сейчас времени?" идеально согласованы (если вы используете FPIM, вы просто смеетесь над двумя соответствующими модулями).

Мне нравится FPIM, потому что на самом деле не так уж много добавленной стоимости, если использовать умноженное квалифицированное имя, а не однозначное (в то время как разница между голыми и квалифицированными именами составляет огромная - - вы получаете , поэтому гораздо больше контроля с определенным именем, будь то по одному или несколько раз, чем вы когда-либо можете с голым именем!).

Ах, хорошо, я не могу посвятить весь рабочий день тому, чтобы отвечать на все ваши вопросы - ваш вопрос, вероятно, должен быть полдюжины вопросов ;-). Я надеюсь, что это, по крайней мере, рассматривает «почему F лучше, чем R» и некоторые проблемы с насмешками / тестированием - все сводится к , сохраняя и улучшая хорошо спроектированную модульность (через F), а не подрывая ее ( через R).

16 голосов
/ 16 ноября 2009

Классический текст на эту тему, как это часто бывает, написан Фредриком Лундом, эффботом. Его совет: всегда используйте импорт - кроме случаев, когда вы не должны.

Другими словами, будьте благоразумны. Лично я нахожу, что все, что имеет глубину в несколько модулей, имеет тенденцию импортироваться через from x.y.z import a - основным примером являются модели Django. Но так же, как и все остальное, это вопрос стиля, и вы должны иметь согласованный, особенно с такими модулями, как datetime, где и модуль, и класс, который он содержит, называются одним и тем же. Вам нужно написать datetime.datetime.now() или просто datetime.now()? (В моем коде всегда первый.)

Пункты 1 и 2 в вашем списке вопросов выглядят одинаково. Динамическая природа Python означает, что довольно просто заменить элемент в пространстве имен модуля независимо от того, какой из методов вы используете. Трудность возникает, если одна функция в модуле ссылается на другую, которую вы хотите смоделировать. В этом случае импорт модуля вместо функций означает, что вы можете сделать module.function_to_replace = myreplacementfunc, и все работает прозрачно - но это так же просто сделать через FPIM, как и через IM.

Я также не понимаю, как пункт 3 имеет какое-либо отношение к чему-либо. Я думаю, что ваш пункт 4, однако, основан на небольшом недоразумении. Ни один из методов, которые вы дадите, не «загрязнит ваше пространство имен». делает , что делает from module import *, когда вы совершенно не представляете, что именно импортируете, и поэтому функции могут появляться в вашем коде, не давая подсказки читателю, откуда они пришли. Это ужасно, и его следует избегать любой ценой.

6 голосов
/ 07 ноября 2013

Великолепные ответы здесь (я их всех проголосовал), и вот мои мысли по этому вопросу:

Во-первых, обращаясь к каждой из ваших пуль:

(Предположительно) Плюсы FMIF:

  • Недостаток кода: более короткие имена функций помогают придерживаться 80 столбцов на строку.

Возможно, но имена модулей обычно достаточно короткие, так что это не имеет значения. Конечно, есть datetime, но также os, re, sys и т. Д. И у Python есть свободные разрывы строк внутри { [ (. А для вложенных модулей всегда есть as в IM и FPIM

  • Удобочитаемость: chisquare (...) выглядит более читабельным, чем scipy.stats.stats.chisquare (...).

Сильно не согласен. При чтении чужого кода (или моего собственного кода через несколько месяцев) трудно понять, откуда взялась каждая функция. Квалифицированные имена спасают меня от перехода от строки 2345 к заголовку объявления модуля. И это также дает вам context : "chisquare? Что это? О, это из scypy? Хорошо, тогда кое-что связанное с математикой" . И, опять же, вы всегда можете сократить scipy.stats.stats as scypyst. scypyst.chisquare(...) достаточно короткий, чтобы получить все преимущества квалифицированного имени.

import os.path as osp - еще один хороший пример, учитывая, что очень распространено объединять 3 или более своих функций вместе в одном вызове: join (expanduser (), basename (splitext ())) и т. Д.

  • Простота перенаправления: переопределение одной строки функции из altmodule вместо модуля.

Как часто вы хотите переопределить одну функцию, но не весь модуль? Границы модуля и координация функций должны быть сохранены, и Алекс уже объяснил это очень подробно. Для большинства (всех?) Реальных сценариев, если alt_module.x является жизнеспособной заменой для module.x, то, вероятно, alt_module сам по себе является альтернативой для module, поэтому и IM, и FPIM являются однострочниками как FMIF, при условии, что вы используете as.

  • Я понимаю, что FPIM каким-то образом сводит на нет первые две проблемы ...

На самом деле, as - это то, что смягчает первые 2 проблемы (и 3-ю), а не FPIM. Вы также можете использовать IM для этого: import some.long.package.path.x as x для того же результата, что и FPIM.


Так что ни один из вышеперечисленных на самом деле не является плюсом FMIF. И причины, по которым я предпочитаю IM / FPIM:

Для простоты и согласованности, когда я импортирую что-либо, IM или FPIM, я всегда импортирую модуль , а не объект из модуля. Помните, что FMIF может (ab-) использоваться для импорта функций, классов, переменных или даже других модулей! Подумайте о беспорядке from somemodule import sys, somevar, os, SomeClass, datetime, someFunc.

Кроме того, если вам требуется более одного объекта из модуля, FMIF будет загрязнять ваше пространство имен больше, чем IM или FPIM, который будет использовать одно имя независимо от того, сколько объектов вы хотите использовать. И такие объекты будут иметь квалифицированное имя, которое является профи, а не мошенничеством: как я уже говорил в выпуске 2, ИМХО это улучшает читаемость.

Все сводится к последовательности, простоте, организации. "Импортируйте модули, а не объекты" - это хорошая и легкая модель для понимания.

5 голосов
/ 16 ноября 2009

Как и Алекс Мартелли, я люблю использовать as при импорте функции.

Одна вещь, которую я сделал, это использование некоторого префикса для всех функций, которые были импортированы из одного и того же модуля:

from random import seed as r_seed
from random import random as r_random

r_seed короче типа, чем random.seed, но в некоторой степени сохраняет границы модуля. Кто-то, небрежно смотрящий на ваш код, может видеть r_seed() и r_random() и иметь шанс уловить, что они связаны.

Конечно, вы всегда можете просто сделать:

import random as r

, а затем используйте r.random() и r.seed(), что может быть идеальным компромиссом для этого случая. Я использую трюк с префиксом, когда импортирую одну или две функции из модуля. Когда я хочу использовать много функций из одного и того же модуля, я импортирую модуль, возможно, с as, чтобы сократить имя.

1 голос
/ 10 ноября 2013

Я согласен с MestreLion здесь больше всего (и так далее).

Моя точка зрения: я часто просматриваю код, который мне незнаком, и не зная, из какого модуля исходит функция, просто глядя на функцию, довольно сложно

Код пишется один раз и читается много раз, поэтому удобочитаемость и удобство обслуживания значительно упрощают ввод текста.

В аналогичном ключе, как правило, код написан не в интересах кодировщика, а в интересах другого объекта.

Ваш код должен быть доступен для чтения тем, кто знает Python лучше вас, но не знаком с кодом.

Импорт полного пути также может помочь IDE указать вам правильный источник функции или объекта, на который вы смотрите.

По всем этим причинам и причинам, отмеченным MestreLion, я заключаю, что рекомендуется импортировать и использовать полный путь.

...