- Importing a module runs code
- A module that defines and prints things
- What happens when you import a module?
- Avoiding import side effects
- When are import side effects acceptable?
- Python runs code when you import a module
- What comes after Intro to Python?
- Series: Modules
- Python import, как и для чего?
- Как использовать import?
- Что можно импортировать?
- В чем же подвох?
- Ветвистая структура приложения и существующие подходы импортирования
- P.S.
Importing a module runs code
Sign in to your Python Morsels account to save your screencast settings.
When Python imports a module, it runs all the code in that module.
A module that defines and prints things
Here we have a file called salutations.py :
import random salutations = ["Hello", "Hey", "Hi", "Hiya", "Howdy"] emphatic_salutations = [] for greeting in salutations: emphatic_salutations.append(greeting + "!") salutations.extend(emphatic_salutations) def greet(): """Print a salutation.""" print(random.choice(salutations)) print("Test calling greet:", greet()) def part(): """Print a valediction.""" print("Live long and prosper") print("Test calling part:", part())
This salutions module imports random and defines a list (named salutations ). It then defines another list named emphatic_salutations (based on the first list) and then it extends that first list.
Then it defines a function called greet , calls that function, and prints a statement. Lastly, it defines another function called part , calls that function, and prints another statement.
What happens when you import a module?
Now, what do you expect would happen if we were to import this salutations module?
Normally, what happens when you import a module in Python is we’d see nothing happen. For example, if we import the math module, we’ll get a math module object back, but we wouldn’t see anything happen at import time.
But when we import salutations , we see things printed out:
>>> import salutations Hiya! Test calling greet: None Live long and prosper Test calling part: None
This happens because when Python imports a module, it runs all the code in that module. After running the module it takes whatever variables were defined in that module, and it puts them on the module object, which in our case is salutations .
So within our salutations module, we have a greet function:
>>> salutations.part() Live long and prosper
We also have a salutations list:
>>> salutations.salutations ['Hello', 'Hey', 'Hi', 'Hiya', 'Howdy', 'Hello!', 'Hey!', 'Hi!', 'Hiya!', 'Howdy!']
In fact we even have a greeting variable:
>>> salutations.greeting 'Howdy'
emphatic_salutations = [] for greeting in salutations: emphatic_salutations.append(greeting + "!") salutations.extend(emphatic_salutations)
Even the random module is available on our salutations module object (because salutations.py imported the random module):
Seeing greeting and random on our salutations object is really weird, but this is just the way Python works.
Avoiding import side effects
When you import a module in Python, all the code in it will be run, and all the variables in that module will be stuck on that module object.
This normally isn’t a problem unless we have side effects at import time. That’s the problem in our case: our salutations module has side effects.
We’re printing a few statements at the top-level of our salutations module:
def greet(): """Print a salutation.""" print(random.choice(salutations)) print("Test calling greet:", greet()) def part(): """Print a valediction.""" print("Live long and prosper") print("Test calling part:", part())
But we probably shouldn’t be printing at the top-level of our file.
If we remove those print calls:
def greet(): """Print a salutation.""" print(random.choice(salutations)) print("Test calling greet:", greet()) def part(): """Print a valediction.""" print("Live long and prosper") print("Test calling part:", part())
And then we import our module in a new Python REPL, we’ll see.
Nothing happens, except that we get a salutations module object, and it has the variables that are available in this module.
A general guideline: when you’re making a Python file (a .py file), you shouldn’t usually put print calls at the top-level of that file. Typically, the only thing you’ll do at the top-level of a module is define functions, classes, or other objects (constants for example) that are meant to be used accessed on your module object (after your module has been imported). But there is one exception to this general rule: command-line programs.
When are import side effects acceptable?
The one place that you actually want to print at the top-level of your module is if you’re making a command-line program (a.k.a. a script).
Here we have a command-line program called greet.py :
import random import sys salutations = ["Hello", "Hey", "Hi", "Hiya", "Howdy"] def greet(name): """Print a salutation.""" print(random.choice(salutations), name) arguments = sys.argv[1:] if not arguments: greet("") for name in arguments: greet(name)
This greet.py file is meant to be used as a command-line program:
$ python3 greet.py Trey Hi Trey
Each time it prints a random greeting to the screen (using the random module):
$ python3 greet.py Trey Hello Trey $ python3 greet.py Howdy
In this Python script we have some code that reads arguments (if they’re given to us) and then calls our greet function (which prints something out):
arguments = sys.argv[1:] if not arguments: greet("") for name in arguments: greet(name)
This code is at the top-level of our module because we want to run this code every time our program runs. This module is not mean to be imported, so print calls at the top-level of our program are acceptable.
Python runs code when you import a module
When you import a module, Python will run all the code that’s in that module.
So if your Python file is meant to be imported as a module, be careful not to put side effects at the top-level of your .py file.
What comes after Intro to Python?
Intro to Python courses often skip over some fundamental Python concepts.
Sign up below and I’ll explain concepts that new Python programmers often overlook.
Series: Modules
Modules are the tool we use for breaking up our code into multiple files in Python. When you write a .py file, you’re making a Python module. You can import your own modules, modules included in the Python standard library, or modules in third-party packages.
To track your progress on this Python Morsels topic trail, sign in or sign up.
Python import, как и для чего?
В языке программирования Python подключение пакетов и модулей осуществляется с помощью import. Это позволяет распределять код по логическим «узлам» приложения(модели данных, обработчики, и тп.), что позволяет получить менее нагруженные кодом файлы.
- Повышается читаемость кода.
- Код логически разбит по «узлам», его поиск и дальнейший отлов ошибок становится понятнее и проще.
- Для разработки в команде это дает более четкое понимание, что и где делает каждый при выполнении «задания».
Как использовать import?
Синтаксис import в Python достаточно прост и интуитивно понятен:
# В данной строке импортируется something_we_want import something_we_want # В данной строке импортируется something_we_want, как aww(логично и просто) import something_we_want as aww # В данной строке импортируется из something_we_want something(логично и просто) from something_we_want import something # В данной строке импортируется из something_we_want something, как s(логично и просто) from something_we_want import something as s # Синтаксис as позволяет обращаться к импортируемому по новому нами описанному # далее имени(это работает только в рамках нашего файла)
Что можно импортировать?
Для более глубокого понимания import стоит рассмотреть пример, представленный ниже.
def something(): pass somedata = 5
# 1 случай import something_we_want something_we_want.something() import something_we_want print(something_we_want.somedata) # 2 случай import something_we_want as aww aww.something() import something_we_want as aww print(aww.somedata) # 3 случай from something_we_want import something something() from something_we_want import somedata print(somedata) # 4 случай from something_we_want import something as s s() from something_we_want import somedata as sd print(sd) # Классы импортируются по аналогии с функциями
Красиво, читаемо и понятно.
В чем же подвох?
Но даже в таком простом примере есть подвох, о котором многие не догадываются(если вы начинающий программист, то лучше перейдите к следующему оглавлению).
Идеология Python достаточно интересна, что позволяет ему иметь низкий порог вхождения, низкое время написания кода, высокую читаемость, но именно в ней и кроется подвох.
По своему опыту использования данного языка, сложилось отчетливое ощущение главной идеи ООП(все есть объект). Что же в этом плохого?
Все файлы, функции и тд. это объект. Но что это за объект и класс стоят за файлами(модулями)?
Все просто, это любимый всеми программистами класс, использующий паттерн проектирования Singleton.
Поэтому при достаточно ветвистой структуре, импорт переменной и дальнейшая ее модификация может порождать достаточно не простые в понимании баги(переменная в своем цикле жизни может иметь любое значение и никаких гарантий нет).
Ветвистая структура приложения и существующие подходы импортирования
Часто в разработке приложений программисты пытаются разбить программу по логическим «узлам». Данный подход повышает читаемость и позволяет вести разработку в команде(один человек занимается реализацией одного «узла», второй другого). Так порождается структура приложения, которая зачастую виду сложности функционала является достаточно обширной(ветвистой, потому что имея одну точку входа откуда уже обрастая функционалом это становится похожим на дерево).
Пример ветвистой структуры:
Существует 2 подхода импортирования(лучше выбрать один и придерживаться его весь проект):
Пример именованного импорта из models.py в auth.py:
# auth.py from app.models import User
Пример неименованного импорта из models.py в auth.py:
# auth.py from ..models import User # Количество точек указывает на сколько (обьектов) мы поднимаемся от исходного. # В данном примере первая точка поднимает нас на уровень обьекта handlers, # А вторая точка поднимает нас на уровень обьекта app
Это два абсолютно разных подхода. В первом случае мы «идем» из «корня»(входной точки нашего приложения). Во втором случае мы «идем» от «листа»(нашего файла).
Плюсы и минусы подходов импорта:
Видна структура импорта и приложения.
Видна часть структуры импорта.
Программисту не нужно знать полную структуру приложения.
Импорт не зависит от точки входа.
Код становится не привязанным к приложению. Что по сути позволяет исполнить код из любой точки(тесты, отдельно и тд.). Повышается отлаживаемость. Появляется возможность разработки отдельных узлов приложения без полного вовлечения программиста в проект.
Импорт зависит от точки входа.
Программисту необходимо знать структуру приложения. Код сильно связан с приложением. Что по сути усложняет отладку, тестирование, и тд. Программист становится сильно вовлеченным в проект.
Снижается читаемость импорта.
Хоть первый подход и имеет существенные минусы в использовании, но тем не менее он популярен. Программистам он привычнее, хоть и имеет недостатки. А начинающие часто не задумываются об альтернативах.
P.S.
Данная статья была написана для начинающих программистов, которые хотят научиться писать на языке программирования Python, поэтому часть примеров заведомо упрощена и нацелена на освещение существующих подходов.
Пишите тот код, который бы сами хотели получить от исполнителя.