Является ли `import module` лучшим стилем кодирования, чем`from module import function`?
Позволять from module import function
можно назвать стилем кодирования FMIF.
Позволять import module
называться стилем кодирования IM.
Позволять from package import module
называться стилем кодирования FPIM.
Почему IM+FPIM считается лучшим стилем кодирования, чем FMIF? (См. Этот пост для вдохновения для этого вопроса.)
Вот некоторые критерии, которые побуждают меня предпочитать FMIF над IM:
- Недостаток кода: он позволяет мне использовать более короткие имена функций и, таким образом, помогает придерживаться соглашения о 80 столбцах на строку.
- Читаемость:
chisquare(...)
кажется более читабельным, чемscipy.stats.stats.chisquare(...)
, Хотя это субъективный критерий, я думаю, что большинство людей согласятся. - Простота перенаправления: если я использую FMIF и по какой-то причине в будущем хочу перенаправить Python для определения
function
отalt_module
вместоmodule
Мне нужно изменить только одну строку:from alt_module import function
, Если бы я использовал IM, мне нужно было бы изменить много строк кода.
Меня интересуют все причины, по которым IM+FPIM может быть лучше, чем FMIF, но, в частности, мне было бы интересно проработать следующие моменты, упомянутые здесь:
Плюсы для IM:
- легкость насмешек / инъекций в тестах. (Я не очень знаком с насмешками, хотя недавно узнал, что означает этот термин. Можете ли вы показать код, который демонстрирует, насколько IM лучше, чем FMIF, здесь?)
- возможность гибкого изменения модуля путем переопределения некоторых записей. (Должно быть, я что-то неправильно понимаю, потому что это, кажется, преимущество FMIF перед IM. См. Мою третью причину в пользу FMIF выше.)
- предсказуемое и контролируемое поведение при сериализации и восстановлении ваших данных. (Я действительно не понимаю, как выбор IM или FMIF влияет на эту проблему. Пожалуйста, уточните.)
- Я понимаю, что FMIF "загрязняет мое пространство имен", но помимо того, что это звучит негативно, я не понимаю, как это повредит коду каким-либо конкретным образом.
Большое спасибо.
5 ответов
Негативы, которые вы перечисляете для 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).
Классический текст об этом, как это часто бывает, написан Фредриком Лундом, специалистом по эфби. Его совет: всегда используйте импорт - кроме случаев, когда вы не должны.
Другими словами, будьте благоразумны. Лично я нахожу, что все, что несколько модулей глубоко, имеет тенденцию импортироваться через 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 *
где вы совершенно не представляете, что именно импортируете, и поэтому функции могут появляться в вашем коде, не давая подсказки читателю, откуда они пришли. Это ужасно, и его следует избегать любой ценой.
Здесь отличные ответы (я их всех одобрил), и вот мои мысли по этому вопросу:
Во-первых, обращаясь к каждой из ваших пуль:
(Предположительно) Плюсы FMIF:
- Короткость кода: более короткие имена функций помогают придерживаться 80 столбцов на строку.
Возможно, но имена модулей обычно достаточно короткие, так что это не имеет значения. Конечно, есть datetime
, но также os
, re
, sys
и т. д. И у Python есть свободные разрывы строк внутри { [ (
, А для вложенных модулей всегда есть as
в IM и FPIM
- Удобочитаемость: chisquare(...) выглядит более читабельным, чем scipy.stats.stats.chisquare(...).
Сильно не согласен. При чтении чужого кода (или моего собственного кода через несколько месяцев) трудно понять, откуда взялась каждая функция. Квалифицированные имена спасают меня от перехода от строки 2345 к заголовку объявлений модуля. И это также дает вам контекст: "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, ИМХО это улучшает читабельность.
все сводится к последовательности, простоте, организации. "Импортировать модули, а не объекты" - это хорошая, легкая в использовании модель.
Как Алекс Мартелли, я люблю использовать 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
сократить имя.
Я согласен с MestreLion больше всего здесь (и поэтому upvote).
Моя точка зрения: я часто просматриваю код, с которым мне незнакомы, и не зная, из какого модуля исходит функция, просто глядя на функцию, довольно сложно.
Код пишется один раз и читается много раз, поэтому удобочитаемость и удобство обслуживания значительно упрощают ввод текста.
В том же духе, как правило, код пишется не для блага кодера, а для блага другого объекта.
Ваш код должен быть доступен для чтения тем, кто знает Python лучше, чем вы, но не знаком с кодом.
Импорт полного пути также может помочь IDE указать вам правильный источник функции или объекта, на который вы смотрите.
По всем этим причинам и по причинам, отмеченным MestreLion, я заключаю, что рекомендуется импортировать и использовать полный путь.