Как стать автором
Обновить

Import or from import, that is the question

Время на прочтение4 мин
Количество просмотров13K

Есть три стадии знаний: ты используешь инструмент, ты понимаешь как он работает, ты можешь учить других работать этим инструментом. Потихонечку начал перетекать в третью и стал задавать себе вопросы, которые раньше не задавал.


Например, что лучше: import module или from module import function?


Я решил разобраться в этом чуть поглубже, ответы на StackOverflow меня не удовлетворили.


Для тех, кому лень читать: все варианты хороши.



Советы от создателей языка


Обратимся к первоисточникам, а именно — к PEP8


Когда импортируете класс из модуля, будет нормально сделать это так:


from myclass import MyClass
from foo.bar.yourclass import YourClass

Если это вызывает локальный конфликт имён, то можно сделать это явно:


import myclass
import foo.bar.yourclass

И использовать как myclass.MyClass и foo.bar.yourclass.YourClass
PEP-8 поддерживает оба подхода и даёт некоторые рекомендации.


Кто принимает решение?


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


Документация


Самый очевидный способ продемонстрировать как пользоваться библиотекой, — документация. Не ту документацию, которую никто не читает, а примеры использования.


"Фляжка" и "бутылка" идут по пути точечных импортов.


from flask import Flask
app = Flask(__name__)

@app.route('/')
def hello_world():
    return 'Hello, World!

from bottle import route, run, template

@route('/hello/<name>')
def index(name):
    return template('<b>Hello {{name}}</b>!', name=name)

run(host='localhost', port=8080)

Пайтест выбирает импортирование модуля.


import pytest

def test_zero_division():
    with pytest.raises(ZeroDivisionError):
        1 / 0

В случае пайтеста это вполне логичный выбор, так как эта библиотека должна быть максимально совместима со всем кодом на планете.
У неё один из самых высоких шансов получить локальный конфликт имён.
Есть даже плагин для линтера который проверяет, что импорты оформлены правильно.


Имена атрибутов модуля


Второй подход основан на выборе имён внутри модуля. Для очень абстрактных имён, например как в некоторых модулях стандартной библиотеки лучше использовать импорт из модуля.


import json

json.load(...)
json.loads(...)
json.dump(...)
json.dumps(...)

Без импорта модуля имена теряют контекст и могут внести путаницу.


from pickle import load
from marshal import dumps
from xmlrpc.client import loads
from xml.etree.ElementTree import dump

load без контекста может быть как json.load так, и pickle.load.
При этом вторая функция имеет ограничения по применению в связи безопасностью и ей при просмотре нужно уделить особое внимание. Обратный случай – когда имена модуля и его содержимого очень большие и загромождают код. Короткие, абстрактные имена – это прерогатива создателей стандартной библиотеки языка. Для своего же кода я выбираю имена среднего и большого размера, которые понятны без контекста в виде имени модуля. Но граничные случаи редки, и большинство библиотек не вызывают проблем при использовании любого из подходов.


Секция импортов


Если импортировать атрибуты из модуля, то секция импортов начинает пухнуть. А если через модуль, то пухнет код.


Секция импортов — это та часть кода, которая хорошо поддаётся оптимизации.
Например, некоторые IDE умеет её сворачивать, а так же самостоятельно форматировать. Да и импорты я редко прописываю руками, подсказки по коду предлагают это сделать за меня. Еще можно форматировать импорты сторонними инструментами, например isort.


Лично для меня некоторое многословие в импортах менее страшно чем многословие в коде.


Простота чтения


При импорте отдельных объектов получатся более короткий код. Его проще читать. Меньше переносов строк.


В целом чуда не случается, но читать приятнее.


Рефакторинг


В IDE поддерживаются оба случая на одном уровне, и автоматика работает одинаково. Но иногда бывают сбои: мерджи с другим кодом, ручные переименования/перемещения файлов. Если переименовали объект, а использования забыли, то при импортировании атрибутов код упадёт сразу,
а при доступе через единый модуль – только в момент вызова.


Если у вас 100% покрытие тестами – разницы нет. Если нет, то второй случай пропустить легко.


MonkeyPatch


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


Этот момент – достаточно частый источник недоумения.


Производительность и память


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


Пространство имён


Если в пространстве имён есть конфликты, то Питон позволяет их обойти двумя способами. Либо import module, либо from module import item as module_item. Мне подход с модулем больше нравится, as практически не использую, пусть лучше будет чуть больше кода с именами модулей, чем добавление новых имён. Хотя есть и устоявшиеся традиции.


import matplotlib.pyplot as plt
import numpy as np
import pandas as pd

Единообразие


На мой взгляд, использование обоих подходов не сильно нарушает единообразие, и можно их спокойно смешивать. Желательно, правда, не для одного и того же модуля. Некоторые инструменты на такое ругаются: https://lgtm.com/rules/1818040193/


Заключение


Для себя я выбираю следующий подход при импортировании и написании своих модулей.
Использовать import from, кроме случаев:


  • есть (или вероятен впоследствии) конфликт имён;
  • функции/классы модуля имеют короткие и распространённые имена (json);
  • существуют устоявшиеся традиции или конкретные рекомендации авторов модуля, например pytest.
Теги:
Хабы:
Всего голосов 20: ↑19 и ↓1+18
Комментарии9

Публикации

Истории

Работа

Python разработчик
137 вакансий
Data Scientist
61 вакансия

Ближайшие события