Является ли `import module` лучшим стилем кодирования, чем` из функции импорта модуля '?

Пусть 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.

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

Ответ 1

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

Считайте свою про-FMIF-точку 3 (назовите ее R для перенаправления) против вашей про-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, потому что на самом деле не так много добавленной стоимости, используя многозначное имя, а не однократно (в то время как разница между именами и квалифицированными именами огромна - вы получаете так) больше контроля с квалифицированным именем, будь то по отдельности или размножаться, чем вы, возможно, когда-либо можете с помощью barename!).

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

Ответ 2

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

Другими словами, будьте разумными. Лично я нахожу, что все, что несколько модулей имеют тенденцию получать, импортируются через 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 *, где вы вообще не представляете, что вы импортируете, и поэтому функции могут появляться в вашем коде без каких-либо подсказок, которые даются читателю, откуда они пришли. Это ужасно, и его следует избегать любой ценой.

Ответ 3

Как и Алекс Мартелли, я люблю использовать 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, чтобы сократить имя.

Ответ 4

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

Сначала, обращаясь к каждой из ваших пуль:

(Предположительно) Плюсы 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, которое будет использовать одно имя независимо от того, сколько объектов вы хотите использовать. И такие объекты будут иметь квалифицированное имя, которое является pro, а не con: как я сказал в выпуске 2, IMHO a улучшает читаемость.

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

Ответ 5

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

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

Код записывается один раз и читается много раз, и поэтому читаемость и ремонтопригодность превосходят легкость ввода.

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

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

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

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