Почему «импорт *» плох?


153

Рекомендуется не использовать import *в Python.

Может кто-нибудь, пожалуйста, поделитесь причиной этого, чтобы я мог избежать этого в следующий раз?



2
это зависит от того, пишете ли вы код или пишете код, который вам нужно использовать повторно. иногда стоит игнорировать стандарты кода. «import *» также может подойти, если у вас есть соглашение об именах, которое проясняет, откуда взялись вещи. например, "из импорта кошек *; TabbyCat; MaineCoonCat; CalicoCat;"
gatoatigrado

3
import *не работает для меня в первую очередь в Python 2 или 3.
joshreesjones

1
Отвечает ли это на ваш вопрос? Что именно импортирует * import *?
AMC

Ответы:


223
  • Потому что он помещает много вещей в ваше пространство имен (может скрыть какой-то другой объект из предыдущего импорта, и вы не будете знать об этом).

  • Потому что вы не знаете точно, что импортируется, и не можете легко найти, из какого модуля была импортирована определенная вещь (удобочитаемость).

  • Потому что вы не можете использовать классные инструменты, такие как pyflakesстатическое обнаружение ошибок в вашем коде.


2
Да, я действительно ненавижу свою работу, когда кто-то использует * import, потому что тогда я не могу просто запустить pyflakes и быть счастливым, но должен восстановить этот импорт. Это хорошо, хотя, что с этим pyflakes помогает мне :-)
gruszczy

7
В качестве конкретного примера, многие пользователи NumPy были укушены numpy.anyтенями, anyкогда они это делают from numpy import *или «полезный» инструмент делает это для них.
user2357112 поддерживает Monica

1
Следует ли мне избегать использования ключа --pylab для IPython по тем же причинам?
Timgeb

6
Чтобы подчеркнуть риск, о котором я никогда не думал, прежде чем читать это («может затенить какой-то другой объект из предыдущего импорта»): import *делает порядок из importзаявлений значительных ... даже для стандартных модулей библиотеки , которые обычно не заботятся о порядке ввоза , Нечто столь же невинное, как алфавитизация ваших importзаявлений, может сломать ваш сценарий, когда бывшая жертва войны за импорт станет единственным выжившим. (Даже если ваш сценарий работает сейчас и никогда не изменяется, он может неожиданно завершиться ошибкой спустя некоторое время, если импортированный модуль вводит новое имя, которое заменяет имя, на которое вы полагались.)
Кевин Дж. Чейз,

49

Согласно дзен питона :

Явное лучше, чем неявное.

... не могу с этим поспорить, конечно?


29
На самом деле, вы можете поспорить с этим. Это также полностью противоречиво, учитывая, что вы не объявляете переменные явно в Python, они просто появляются, когда вы присваиваете им.
Конрад Рудольф

7
@gruszczy: объявление переменных избыточно для чего ? Назначение? Нет, это две разные концепции, и объявление чего-то передает очень четкую и важную информацию. Во всяком случае, явность всегда в некоторой степени связана с избыточностью, это две стороны одной медали.
Конрад Рудольф

3
@ kriss верно, но это не моя точка зрения. Моя точка зрения заключалась в том, что отказ явно объявить переменную приводит к ошибкам. Вы говорите, что «назначение без [объявления] невозможно». Но это не так, моя суть в том, что Python, к сожалению, делает именно это возможным.
Конрад Рудольф

3
@kriss Другая часть информации, предоставляемая компилятору объявлением, - это тот факт, что вы действительно намереваетесь объявить новую переменную. Это важная часть информации для системы типов. Вы говорите, что современные IDE решают проблему опечаток, но это просто неправильно, и на самом деле это существенная проблема в не статически скомпилированных языках, именно поэтому Perl добавил use strict(JavaScript var). Кроме того, конечно, Python не является типизированным (на самом деле он строго типизирован). В любом случае, даже если вы были правы, это все равно будет противоречить дзен Python, приведенному в этом ответе.
Конрад Рудольф

3
@kriss Вы ошибаетесь: повторное использование одного и того же имени переменной не является проблемой - повторное использование одной и той же переменной (то есть одно и то же имя в той же области видимости). Явные объявления предотвратят именно эту ошибку (и другие, основанные на простом опечатке, которая, как я уже сказал, на самом деле является чрезвычайно распространенной и трудоемкой проблемой, даже если вы правы, что проблема больше в Perl-подобной языки). И противоречие, на которое я намекаю, - это требование дзен к явности, которое здесь физически выбрасывается из окна.
Конрад Рудольф

40

Вы не проходите **locals() к функциям, не так ли?

Поскольку Python отсутствует оператор «включить», иself параметр является явным, и Scoping правила довольно просты, это, как правило , очень легко указать пальцем на переменную и сказать , где этот объект приходит от - без чтения других модулей и без каких - либо IDE (которые в любом случае ограничены в способе самоанализа, потому что язык очень динамичен).

import * перерывах все это.

Кроме того, он имеет конкретную возможность скрывать ошибки.

import os, sys, foo, sqlalchemy, mystuff
from bar import *

Теперь, если в модуле bar есть какие-либо атрибуты " os", " mystuff" и т. Д. ..., они переопределят явно импортированные и, возможно, укажут на совершенно разные вещи. Определение __all__в строке часто целесообразно - здесь указывается, что будет импортироваться неявно, - но все же сложно отследить, откуда берутся объекты, не читая и не анализируя модуль бара и не следуя его импорту. Сетьimport * - это первое, что я исправляю, когда беру на себя ответственность за проект.

Не поймите меня неправильно: если бы import *не хватало, я бы заплакал, чтобы получить это. Но это должно быть использовано осторожно. Хорошим вариантом использования является предоставление интерфейса фасада поверх другого модуля. Аналогично, использование операторов условного импорта или импорта внутри пространств имен функций / классов требует некоторой дисциплины.

Я думаю, что в средних и больших проектах, или небольших, с несколькими участниками, требуется минимум гигиены с точки зрения статического анализа - запуск по крайней мере pyflakes или даже лучше правильно настроенного pylint - чтобы поймать несколько видов ошибок до они случаются.

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


6
Python 2.x делает есть «включить» заявление. Это называется execfile(). К счастью, он редко используется и пропал в 3.x.
Свен Марнач

Как насчет **vars()включения глобальных переменных, если вызываемая функция находится в другом файле? : P
Соломон Уцко

16

Это нормально делать from ... import *в интерактивном сеансе.


Как насчет doctestстроки? В import *этом случае интерпретируется ли «песочница»? Спасибо.
PatrickT

16

Это потому, что вы загрязняете пространство имен. Вы импортируете все функции и классы в собственное пространство имен, которое может конфликтовать с функциями, которые вы определяете сами.

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

В модуле foo:

def myFunc():
    print 1

В вашем коде:

from foo import *

def doThis():
    myFunc() # Which myFunc is called?

def myFunc():
    print 2


9

Скажем, у вас есть следующий код в модуле с именем foo:

import ElementTree as etree

и тогда в вашем собственном модуле у вас есть:

from lxml import etree
from foo import *

Теперь у вас есть трудный для отладки модуль, который выглядит так, как будто в нем есть этри из lxml, но на самом деле вместо него есть ElementTree.


7

Это все хорошие ответы. Я собираюсь добавить, что при обучении новых людей программировать на Python, имея дело сimport * очень сложно. Даже если вы или они не написали код, это все равно камень преткновения.

Я учу детей (около 8 лет) программировать на Python, чтобы манипулировать Minecraft. Мне нравится давать им полезную среду программирования для работы с ( Atom Editor ) и обучать разработке на основе REPL (через bpython ). В Atom я обнаружил, что подсказки / дополнения работают так же эффективно, как bpython. К счастью, в отличие от некоторых других инструментов статистического анализа, Atom не одурачен import *.

Однако, давайте возьмем этот пример ... В этой обертке они from local_module import *связывают модули, включая этот список блоков . Давайте проигнорируем риск коллизий пространства имен. Делая это, from mcpi.block import *они превращают весь этот список непонятных типов блоков в то, что вам нужно посмотреть, чтобы узнать, что доступно. Если бы они вместо этого использовали from mcpi import block, то вы могли бы напечатать, walls = block.а затем появится список автозаполнения. Скриншот Atom.io


6

Понял действительные баллы, которые люди ставят здесь. Однако у меня есть один аргумент, что иногда «импорт звезд» не всегда является плохой практикой:

  • Когда я хочу структурировать свой код таким образом, чтобы все константы шли в модуль с именем const.py :
    • Если да import const, то для каждой константы я должен ссылаться на нее const.SOMETHING, что, вероятно, не самый удобный способ.
    • Если я это сделаю from const import SOMETHING_A, SOMETHING_B ..., то, очевидно, это слишком многословно и побеждает цель структурирования.
    • Таким образом, я чувствую, что в этом случае from const import *лучше сделать выбор.

4

Это очень плохая практика по двум причинам:

  1. Читаемость кода
  2. Риск переопределения переменных / функций и т. Д.

Для пункта 1 : Давайте посмотрим пример этого:

from module1 import *
from module2 import *
from module3 import *

a = b + c - d

Здесь нет, видя код никто не будет получить представление о от того, какой модуль b, cиd на самом деле принадлежит.

С другой стороны, если вы делаете это так:

#                   v  v  will know that these are from module1
from module1 import b, c   # way 1
import module2             # way 2

a = b + c - module2.d
#            ^ will know it is from module2

Это намного чище для вас, и у нового человека, присоединившегося к вашей команде, будет лучшее представление.

Для пункта 2 : пусть говорят оба module1и module2имеют переменную как b. Когда я делаю:

from module1 import *
from module2 import *

print b  # will print the value from module2

Здесь значение из module1теряется. Будет трудно отладить, почему код не работает, даже если bон объявлен, module1и я написал код, ожидающий использования моего кодаmodule1.b

Если у вас есть одни и те же переменные в разных модулях, и вы не хотите импортировать весь модуль, вы можете даже сделать:

from module1 import b as mod1b
from module2 import b as mod2b

2

В качестве теста я создал модуль test.py с 2 функциями A и B, которые соответственно выдают «A 1» и «B 1». После импорта test.py с помощью:

import test

, , , Я могу запустить две функции как test.A () и test.B (), и «test» отображается как модуль в пространстве имен, поэтому, если я отредактирую test.py, я могу перезагрузить его с помощью:

import importlib
importlib.reload(test)

Но если я сделаю следующее:

from test import *

нет ссылки на «тест» в пространстве имен, поэтому нет способа перезагрузить его после редактирования (насколько я могу судить), что является проблемой в интерактивном сеансе. Принимая во внимание одно из следующего:

import test
import test as tt

добавит «test» или «tt» (соответственно) в качестве имен модулей в пространство имен, что позволит перезагрузить.

Если я сделаю:

from test import *

имена «A» и «B» отображаются в пространстве имен как функции . Если я отредактирую test.py и повторю приведенную выше команду, измененные версии функций не будут перезагружены.

И следующая команда вызывает сообщение об ошибке.

importlib.reload(test)    # Error - name 'test' is not defined

Если кто-то знает, как перезагрузить модуль, загруженный "из модуля импорта *", пожалуйста, напишите. В противном случае это будет еще одна причина, чтобы избежать формы:

from module import *

2

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

Хотя импорт *из модуля плох, импорт * из пакета еще хуже. По умолчанию from package import *импортирует любые имена, определенные пакетом __init__.py, включая любые подмодули пакета, которые были загружены предыдущими importоператорами.

Однако, если __init__.pyкод пакета определяет именованный список __all__, он считается списком имен подмодулей, которые следует импортировать при from package import *обнаружении.

Рассмотрим этот пример (при условии, что он не __all__определен в sound/effects/__init__.py):

# anywhere in the code before import *
import sound.effects.echo
import sound.effects.surround

# in your module
from sound.effects import *

Последнее утверждение будет импортировать echoи surroundмодули в текущее пространство имен (возможно перекрывая предыдущие определения) , поскольку они определены в sound.effectsпакете , когда importвыполняются утверждение.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.