Чому "імпорт *" поганий?


153

Рекомендується не використовувати import *в Python.

Хтось може, будь ласка, поділитися причиною цього, щоб я міг уникнути цього наступного разу?



2
це залежить від того, чи ви пишете сценарій чи пишете код, який потрібно повторно використовувати. іноді платять ігнорувати стандарти коду. "import *" також може бути чудовим, якщо у вас є угода про іменування, яка дає зрозуміти, звідки взялися речі. наприклад "з імпорту котів *; TabbyCat; MaineCoonCat; CalicoCat;"
gatoatigrado

3
import *не працює для мене в першу чергу в Python 2 або 3.
joshreesjones

1
Чи відповідає це на ваше запитання? Що саме імпортує "імпорт *"?
AMC

Відповіді:


223
  • Оскільки він містить багато речей у вашій області імен (може затінити якийсь інший об'єкт із попереднього імпорту, і ви про нього не дізнаєтесь).

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

  • Тому що ви не можете використовувати класні інструменти, як, наприклад, pyflakesстатичне виявлення помилок у коді.


2
Так, я дуже ненавиджу свою роботу, коли хтось використовує імпорт *, тому що тоді я не можу просто запускати піфляки і бути щасливим, але мушу відремонтувати цей імпорт. Хоча приємно, що з цими піфляками допомагає мені :-)
gruszczy

7
Як конкретний приклад, багато користувачів NumPy були покусані numpy.anyтіні, anyколи вони це роблять, from numpy import *або "корисний" інструмент робить це для них.
user2357112 підтримує Monica

1
Чи слід уникати використання перемикача --pylab для IPython з тих же причин?
timgeb

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

49

Відповідно до дзен Python :

Явне краще, ніж неявне.

... не може з цим посперечатися, напевно?


29
Власне, можна з цим посперечатися. Це також абсолютно непослідовно, враховуючи те, що ви не оголошуєте змінні явно в Python, вони просто виникають, як тільки ви призначаєте їх.
Конрад Рудольф

7
@gruszczy: оголошення змінних зайве для чого ? Призначення? Ні, це дві окремі концепції і декларація про щось передає дуже чітку та важливу інформацію. У всякому разі, явність завжди дещо пов’язана із надмірністю, вони два обличчя однієї монети.
Конрад Рудольф

3
@kriss вірно, але це не було моєю суттю. Моя думка полягала в тому, що неможливість явного оголошення змінної призводить до помилок. Ви кажете, що "призначення без [декларації] неможливо". Але це неправильно, вся суть у тому, що Python, на жаль, робить саме це можливим.
Конрад Рудольф

3
@kriss Ще однією інформацією, наданою компілятором декларацією, є той факт, що ви дійсно маєте намір оголосити нову змінну. Це важлива інформація для типової системи. Ви говорите, що сучасні ІДЕ вирішують проблему замикання, але це просто неправильно, і насправді це суттєва проблема нестатично складених мов, саме тому Perl додав use strict(JavaScript var). З іншого боку, Python, звичайно, не є типовим (він насправді сильно набраний). У будь-якому випадку, навіть якби ви мали рацію, це все одно суперечить дзену Питона, про який йдеться у цій відповіді.
Конрад Рудольф

3
@kriss Ви помилилися: повторне використання того ж імені змінної не є проблемою - повторне використання тієї самої змінної є (тобто те саме ім'я в тій самій області). Явні декларації запобігли б саме цій помилці (та ін., Що базується на простому введенні помилок, що, як я вже сказав, насправді є надзвичайно поширеною та трудомісткою проблемою, навіть якщо ви маєте рацію, що проблема в Perl схожа мови). І суперечність, на яку я натякаю, - це вимога Дзен про явність, яке тілесно викидається тут з вікна.
Конрад Рудольф

40

Ви не переходите **locals()до функцій, чи не так?

Оскільки Python відсутня оператор «включити», іself параметр є явним, і Scoping правила досить прості, це, як правило , дуже легко вказати пальцем на змінну і сказати , де цей об'єкт приходить від - без читання інших модулів і без будь - яких IDE (які так чи інакше обмежені способом самоаналізу, адже мова є дуже динамічною).

The import * перервах все це.

Також він має конкретну можливість приховування клопів.

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

Тепер, якщо в панельному модулі є який-небудь з атрибутів " os", " mystuff" і т. Д. ..., вони замінять явно імпортовані і, можливо, вказують на дуже різні речі. Визначення __all__в рядку часто є розумним - це говорить про те, що імпліцитно імпортуватиметься, - але все ж важко простежити, звідки беруться об'єкти, не читаючи та розбираючи модуль бару та слідкуючи за його імпортом. Мережа import *- це перше, що я фіксую, коли приймаю право власності на проект.

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

Я думаю, що в середніх і великих проектах, або малих з кількома учасниками, необхідний мінімум гігієни з точки зору статичного аналізу - запуску принаймні піфляк або, ще краще, правильно налаштованого пілінта - щоб зловити кілька помилок раніше вони бувають.

Звичайно, оскільки це пітон - сміливо порушуйте правила та досліджуйте, - але будьте обережні до проектів, які можуть зрости вдесятеро, якщо у вихідному коді відсутня дисципліна, це буде проблемою.


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

Це дуже BAD практика з двох причин:

  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 з двома функціями A і B, які відповідно друкують "A 1" і "B 1". Після імпорту test.py за допомогою:

import test

. . . Я можу запустити 2 функції як 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.