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


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.