- PVSM.RU - https://www.pvsm.ru -
Есть три стадии знаний: ты используешь инструмент, ты понимаешь как он работает, ты можешь учить других работать этим инструментом. Потихонечку начал перетекать в третью и стал задавать себе вопросы, которые раньше не задавал.
Например, что лучше: import module
или from module import function
?
Я решил разобраться в этом чуть поглубже, ответы на StackOverflow меня не удовлетворили.
Для тех, кому лень читать: все варианты хороши.
Обратимся к первоисточникам, а именно — к PEP8 [1]
Когда импортируете класс из модуля, будет нормально сделать это так:
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
В случае пайтеста это вполне логичный выбор, так как эта библиотека должна быть максимально совместима со всем кодом на планете.
У неё один из самых высоких шансов получить локальный конфликт имён.
Есть даже плагин [2] для линтера который проверяет, что импорты оформлены правильно.
Второй подход основан на выборе имён внутри модуля. Для очень абстрактных имён, например как в некоторых модулях стандартной библиотеки лучше использовать импорт из модуля.
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 [3].
Лично для меня некоторое многословие в импортах менее страшно чем многословие в коде.
При импорте отдельных объектов получатся более короткий код. Его проще читать. Меньше переносов строк.
В целом чуда не случается, но читать приятнее.
В IDE поддерживаются оба случая на одном уровне, и автоматика работает одинаково. Но иногда бывают сбои: мерджи с другим кодом, ручные переименования/перемещения файлов. Если переименовали объект, а использования забыли, то при импортировании атрибутов код упадёт сразу,
а при доступе через единый модуль – только в момент вызова.
Если у вас 100% покрытие тестами – разницы нет. Если нет, то второй случай пропустить легко.
При написании тестов порой надо подменить какие-то методы. Вот тут с импортами из модуля начинается цирк. К моменту, когда накладывается патч, ссылка на объект уже имеется в тех местах, где он импортирован, и патчить нужно не место, где объект объявлен, а все места, где был сделан импорт.
Несложно, но немного запарно, и есть шанс что-то поломать при рефакторинге.
Этот момент – достаточно частый источник недоумения.
Бытует миф, что импортирование из модуля только части классов/функций экономит память. На деле в обоих случаях в память загружается модуль целиком, и разницы в производительности нет.
Если в пространстве имён есть конфликты, то Питон позволяет их обойти двумя способами. Либо 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/ [4]
Для себя я выбираю следующий подход при импортировании и написании своих модулей.
Использовать import from
, кроме случаев:
json
);pytest
.Автор: Andrey_Solomatin
Источник [5]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/python/361878
Ссылки в тексте:
[1] PEP8: https://www.python.org/dev/peps/pep-0008/#imports
[2] плагин: https://github.com/m-burst/flake8-pytest-style/blob/v1.3.0/docs/rules/PT013.md
[3] isort: https://pypi.org/project/isort/
[4] https://lgtm.com/rules/1818040193/: https://lgtm.com/rules/1818040193/
[5] Источник: https://habr.com/ru/post/543832/?utm_source=habrahabr&utm_medium=rss&utm_campaign=543832
Нажмите здесь для печати.