«модуль импорта» и «функция импорта модуля»


143

Я всегда использовал этот метод:

from sys import argv

и использовать argvтолько с argv . Но есть соглашение об использовании этого:

import sys

и используя argv sys.argv

Второй метод делает код самостоятельно документированным, и я (действительно) придерживаюсь его. Но причина, по которой я предпочитаю первый метод, заключается в том, что он быстрый, потому что мы импортируем только ту функцию, которая необходима, а не импортируем весь модуль (который содержит больше бесполезных функций, которые Python будет тратить впустую время, импортируя их). Обратите внимание, что мне нужен только argv, а все остальные функции из sys для меня бесполезны.

Итак, мои вопросы. Первый метод действительно делает скрипт быстрым? Какой метод является наиболее предпочтительным? Почему?



Ответы:


175

Импорт модуль не тратит ничего ; модуль всегда полностью импортируется (в sys.modulesотображение), поэтому вы не используете его import sysили from sys import argvне делаете разногласий.

Единственная разница между этими двумя утверждениями заключается в том, какое имя связано; import sysпривязывает имя sysк модулю (так sys-> sys.modules['sys']), в то время как from sys import argvсвязывает другое имя argv, указывая прямо на атрибут, содержащийся внутри модуля (так argv-> sys.modules['sys'].argv). Остальная часть sysмодуля все еще там, используете ли вы что-нибудь еще из модуля или нет.

Также нет разницы в производительности между этими двумя подходами. Да, sys.argvдолжен искать две вещи; он должен искать sysв вашем глобальном пространстве имен (находит модуль), а затем искать атрибут argv. И да, используя его, from sys import argvвы можете пропустить поиск атрибута, поскольку у вас уже есть прямая ссылка на атрибут. Но importоператор все еще должен выполнять эту работу, он ищет тот же атрибут при импорте, и вам нужно будет использовать его argv только один раз . Если бы вам пришлось использовать argvтысячи раз в цикле, это могло бы иметь значение, но в данном конкретном случае это действительно не так.

Выбор между тем или другим должен основываться на стиле кодирования .

В большом модуле я бы обязательно использовал import sys; Документация по коду имеет значение, и использование sys.argvгде-то в большом модуле делает намного более понятным то, на что вы ссылаетесь, чем argvкогда-либо.

Если единственное место, которое вы используете, argvнаходится в '__main__'блоке для вызова main()функции, во что from sys import argvбы то ни стало используйте, если вы чувствуете себя счастливее по этому поводу:

if __name__ == '__main__':
    from sys import argv
    main(argv)

Я бы все еще использовал import sysтам сам. При прочих равных условиях (и они, в точности, с точки зрения производительности и количества символов, использованных для его написания), для меня это просто на глаз.

Если вы импортируете что - то другое , то, возможно, производительность вступит в игру. Но только если вы используете определенное имя в модуле много раз , например в критическом цикле. Но тогда создание локального имени (внутри функции) будет еще быстрее:

 import somemodule

 def somefunction():
      localname = somemodule.somefunctionorother
      while test:
          # huge, critical loop
          foo = localname(bar)

1
Существует также ситуация, когда у вас есть пакет с подпакетами или модулями, который предоставляет атрибут одного из этих подпакетов / модулей в пакете верхнего уровня. Использование from...importпозволяет делать package.attributeбольше package.subpackage_or_module.attribute, чем , что может быть полезно, если у вас есть логические или концептуальные группировки внутри пакета, но вы хотите сделать вещи немного более удобными для пользователей вашего пакета. ( numpyделает что-то подобное, я полагаю.)
JAB

В django есть множество мест, где такие вещи from django.core.management.base import BaseCommandлучше, и все остальное (особенно import django) может привести к нечитаемому коду. Поэтому, хотя мне нравится этот ответ, я думаю, что есть некоторые библиотеки (и особенно некоторые фреймворки), в которых соглашение нарушает простой импорт. Как всегда, используйте свое суждение о том, что лучше в данной ситуации. Но ошибка на стороне явного (другими словами, я согласен по большей части).
нейронет

1
@JAB: вы можете использовать , import ... asчтобы найти пакет с другим именем: import package.subpackage_or_module as shortname. from parent import subделает, по сути, то же самое.
Мартин Питерс

43

Есть две причины в пользу использования, import moduleа не from module import function.

Во-первых, это пространство имен. Импорт функции в глобальное пространство имен может привести к конфликту имен.

Второе не имеет отношения к стандартным модулям, но важно для ваших собственных модулей, особенно во время разработки. Это вариант для reload()модуля. Учти это:

from module import func
...
reload(module)
# func still points to the old code

С другой стороны

import module
...
reload(module)
# module.func points to the new code

Что касается скорости ...

мы импортируем только ту функцию, которая необходима, а не импортируем весь модуль (который содержит больше бесполезных функций, которые Python будет тратить впустую время, импортируя их)

Независимо от того, импортируете ли вы модуль или импортируете функцию из модуля, Python проанализирует весь модуль. В любом случае модуль импортируется. «Импорт функции» - это не что иное, как привязка функции к имени. На самом деле import moduleэто меньше работы для переводчика, чем from module import func.


6
reload () была встроена в Python 2; это больше не относится к Python 3.
Андре

Я думал, что есть также последствия, связанные с циклическими зависимостями импорта?
ADP

18

Я использую from imports всякий раз, когда это улучшает читабельность. Например, я предпочитаю (точки с запятой здесь только для экономии места):

from collections import defaultdict
from foomodule import FooBar, FooBaz
from twisted.internet.protocol import Factory
defaultdict(); FooBar(); FooBaz(); Factory()

вместо:

import collections
import foomodule
import twisted.internet.protocol
collections.defaultdict(); foomodule.FooBar(); foomodule.FooBaz()
twisted.internet.protocol.Factory()

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

Я предпочитаю обычные imports, если я использую много коротких имен из модуля:

import sys
sys.argv; sys.stderr; sys.exit()

Или, если имя настолько универсально, что не имеет смысла вне его пространства имен:

import json
json.loads(foo)

from json import loads
loads(foo)  # potentially confusing

Это мой любимый ответ. «Явный лучше, чем неявный» иногда конфликтует с удобочитаемостью, простотой и СУХОЙ. Особенно при использовании таких фреймворков, как Django.
нейронет

18

На мой взгляд, использование регулярных importулучшает читабельность. При просмотре кода Python мне нравится видеть, откуда данная функция или класс происходят именно там, где она используется. Это спасает меня от прокрутки к началу модуля, чтобы получить эту информацию.

Что касается длинных имен модулей, я просто использую asключевое слово и даю им короткие псевдонимы:

import collections as col
import foomodule as foo
import twisted.internet.protocol as twip

my_dict = col.defaultdict()
foo.FooBar()
twip_fac = twip.Factory()

В качестве исключения я всегда использую from module import somethingобозначения, когда имею дело с __future__модулем. Вы просто не можете сделать это по-другому, если хотите, чтобы все строки были по умолчанию в Unicode в Python 2, например

from __future__ import unicode_literals
from __future__ import print_function

Аминь! «import as» - выигрышная комбинация :-)
paj28

4

Хотя import sysи from sys import agrvкак импортировать весь sysмодуль, последний использует имя связывания так только argvмодуль доступен для остальной части кода.

Для некоторых это предпочтительный стиль, поскольку он делает доступной только функцию, которую вы явно указали.

Это, однако, вводит потенциальные конфликты имен. Что если у вас есть другой модуль с именем argv? Обратите внимание, что вы также можете явно импортировать функцию и переименовать с from sys import argv as sys_argvпомощью соглашения, которое соответствует явному импорту и с меньшей вероятностью приведет к коллизиям пространства имен.


2
Так как же if sys_argv:лучше чем if sys.argv:? Я знаю, что означает второе утверждение, я понятия не имею, что означает первая форма, не возвращаясь к странному импорту.
MSS

1

Я недавно задал этот вопрос себе. Я рассчитал разные методы.

библиотека запросов

def r():
    import requests
    return 'hello'
timeit r() # output: 1000000 loops, best of 3: 1.55 µs per loop

def rg():
    from requests import get
    return 'hello'
timeit rg() # output: 100000 loops, best of 3: 2.53 µs per loop

красивая библиотека

def bs():
    import bs4
    return 'hello' 
timeit bs() # output: 1000000 loops, best of 3: 1.53 µs per loop

def be():
    from bs4 import BeautifulSoup
    return 'hello'
timeit be() # output: 100000 loops, best of 3: 2.59 µs per loop

библиотека JSON

def js():
    import json
    return 'hello'
timeit js() # output: 1000000 loops, best of 3: 1.53 µs per loop

def jl():
    from json import loads
    return 'hello'
timeit jl() # output: 100000 loops, best of 3: 2.56 µs per loop

библиотека sys

def s():
    import sys
    return 'hello'
timeit s() # output: 1000000 loops, best of 3: 1.55 µs per loop

def ar():
    from sys import argv
    return 'hello'
timeit ar() # output: 100000 loops, best of 3: 2.87 µs per loop

Мне кажется, что есть небольшая разница в производительности.


Вы добавляете в поиск атрибутов. Для сравнения import moduleс from module import nameправильно добавить , что поиск имени в import moduleслучае. Например, добавьте строку sys.argvв arтест и т. Д. Разница все равно будет существовать, потому что проделанная работа немного отличается, поскольку генерируется другой байт-код и выполняются разные пути кода.
Мартин Питерс

2
Обратите внимание, что я прямо исправляю эту разницу в своем ответе; будет разница между использованием и import sysиспользованием sys.argvтысяч раз в цикле по сравнению from sys import argvс использованием просто argv. Но ты не. Для вещей, которые вы делаете только один раз на глобальном уровне вашего модуля, вы действительно должны оптимизировать для удобства чтения, а не микроскопических различий во времени.
Мартейн Питерс

1
Ааааа! И я думал, что я был на что-то! :) Я только просмотрел твой ответ. Похоже, я бросил пистолет на этом. Приятно быть униженным.
tmthyjames

-1

Просмотр опубликованных фрагментов кода, импорт целых модулей и ссылки на module.functionних в значительной степени являются стандартом, по крайней мере для стандартных модулей. Единственное исключение, кажется,datetime

from datetime import datetime, timedelta

так что вы можете сказать, datetime.now()а не datetime.datetime.now().

Если вы беспокоитесь о производительности, вы всегда можете сказать (например)

argv = sys.argv

а затем выполните критический для производительности код, поскольку поиск модуля уже выполнен. Однако, хотя это будет работать с функциями / методами, большинство IDE будут сбиты с толку и не будут (например) отображать исходную ссылку / подпись для функции, когда она назначается переменной.


-2

Я просто хочу добавить, что если вы делаете что-то вроде

from math import sin

(или любая другая встроенная библиотека, такая как sysили posix), затем sinбудет включена в документацию для вашего модуля (то есть, когда вы делаете >>> help(mymodule)или $ pydoc3 mymodule. Чтобы избежать этого, импортируйте, используя:

import math
from math import sin as _sin

PS: встроенная библиотека - это библиотека, которая скомпилирована из кода C и включена в Python. argparse, osА ioне встроенные в пакетах

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