Лайфхаки

Маленькие, полезные хитрости

Разделение кода на модули и пакеты

17.10.2024 в 20:22

Разделение кода на модули и пакеты

В книге “Совершенный Код” Стив Макконнелл формулирует главный технический императив программирования – это управление сложностью . Основная суть, которого заключается в том, что на каждом этапе разработки ПО мы должны прикладывать максимум усилий для того чтобы сложность нашего проекта не “вышла из берегов”. Показателем этого является возможность одновременно удержать в голове основные компоненты проекта на всех уровнях абстракции. В моделировании систем (да и не только там) выделят такой инструмент как декомпозиция – разделение целого на части, этот принцип является одним из наиболее часто используемых способов работать со сложностью. Декомпозицию можно делать на логическом и на физическом уровне. Для реализации последней цели (декомпозиция на физическом уровне) в программном проекте на Python могут служить модули и пакеты.

Для простоты понимания, того, что будет изложено далее, под термином модуль в Python мы будем понимать файл с исходным кодом, имеющий расширение .py ,  а пакет – это каталог, который может включать другие каталоги или модули.

Модуль может содержать функции (например, модуль с тригонометрическими функциями), классы, объекты классов и переменные – все это будет доступно для других модулей, если правильно импортировать данный. Также в модуле может содержаться программный код, который будет выполняться, если скрипт запустить на исполнение.

Следует заметить, что вы можете импортировать как весь модуль целиком, так и избранные его части.

Связанные вопросы и ответы:

Вопрос 1: Что такое модуль в Python

Ответ: Модуль в Python — это файл с расширением .py, содержащий определенные функции, классы и переменные. Модули можно использовать для организации кода и разделения его на логические блоки.

Вопрос 2: Как импортировать модуль в Python

Ответ: В Python можно импортировать модуль с помощью оператора import. Например, если у нас есть модуль my_module.py, то мы можем импортировать его в другом файле с помощью команды import my_module.

Вопрос 3: Что такое пакет в Python

Ответ: Пакет в Python — это структура, состоящая из нескольких модулей и подпакетов. Пакеты можно использовать для организации кода в более крупных проектах.

Вопрос 4: Как создать пакет в Python

Ответ: Чтобы создать пакет в Python, необходимо создать директорию с именем пакета и внутри нее создать файл __init__.py. В этом файле можно определить функции и классы, которые будут доступны после импорта пакета.

Вопрос 5: Что такое пространства имён в Python

Ответ: Пространство имён в Python — это область, в которой определены переменные, функции и классы. Пространства имён можно использовать для организации кода и предотвращения конфликтов имен.

Вопрос 6: Как использовать пространства имён в Python

Ответ: В Python можно использовать пространства имён с помощью оператора dot (точка). Например, если у нас есть модуль my_module.py, который содержит функцию my_function, то мы можем вызвать эту функцию с помощью команды my_module.my_function().

Вопрос 7: Что такое глобальное пространство имён в Python

Ответ: Глобальное пространство имён в Python — это пространство имён, которое доступно во всем коде. В глобальном пространстве имён можно определить переменные, функции и классы, которые будут доступны во всем коде.

Вопрос 8: Как использовать глобальное пространство имён в Python

Ответ: В Python можно использовать глобальное пространство имён с помощью ключевого слова global. Например, если у нас есть глобальная переменная my_variable, то мы можем изменить ее значение с помощью команды global my_variable; my_variable = new_value.

Что такое модуль в Python

Модуль - отдельный файл с кодом на Python, содержащий функции и данные:

    имеет расширение *.py (имя файла является именем модуля);

    может быть импортирован (подключен) (директива import … );

    может быть многократно использован.

Пакеты в Python - это способ структуризации модулей. Пакет представляет собой папку, в которой содержатся модули и другие пакеты и обязательный файл __init.py__ , отвечающий за инициализацию пакета.

Так, например, пакетимеет следующую структуру:

C:\USERS\YURI\APPDATA\LOCAL\PROGRAMS\PYTHON\PYTHON35\LIB\xml │ __init__.py Файл инициализации пакета xml │ ├───dom Вложенный пакет xml.dom │ domreg.py │ expatbuilder.py │ minicompat.py │ minidom.py │ NodeFilter.py │ pulldom.py │ xmlbuilder.py │ __init__.py │ ├───etree Вложенный пакет xml.etree │ cElementTree.py │ ElementInclude.py │ ElementPath.py │ ElementTree.py │ __init__.py │ ├───parsers Вложенный пакет xml.parsers │ expat.py │ __init__.py │ └───sax Вложенный пакет xml.sax expatreader.py handler.py saxutils.py xmlreader.py _exceptions.py __init__.py

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

Одна из основных целей использования как модулей, так и пакетов - реализация модели, позволяющей логически группировать и в то же время изолировать различные идентификаторы. Например, при наличии глобальной переменной author в модуле A и B не произойдет конфликта, т.к. они находятся в разном пространстве имен: A.author и B.author соответственно.

Все модули/пакеты в Python можно разделить на 4 категории:

    Встроенные ( англ. Built-in).

    Модули, встроенные в язык и предоставляющие базовые возможности языка (написаны на языке Си).

    К встроенным относятся как модули общего назначения (например,или), так и плаиформозависимые модули (например, модуль winreg , предназначенный для работы с реестром ОС Windows , устанавливается только на соответствующей ОС).

    Список установленных встроенных модулей можно посмотреть следующим образом:

    >>> import sys >>> >>> print ( sys . builtin_module_names ) # Встроенные модули ('_ast', '_bisect', '_codecs', '_codecs_cn', '_codecs_hk', '_codecs_iso2022', '_codecs_jp', '_codecs_kr', '_codecs_tw', '_collections', '_csv', '_datetime', '_functools', '_heapq', '_imp', '_io', '_json', '_locale', '_lsprof', '_md5', '_multibytecodec', '_opcode', '_operator', '_pickle', '_random', '_sha1', '_sha256', '_sha512', '_signal', '_sre', '_stat', '_string', '_struct', '_symtable', '_thread', '_tracemalloc', '_warnings', '_weakref', '_winapi', 'array', 'atexit', 'audioop', 'binascii', 'builtins', 'cmath', 'errno', 'faulthandler', 'gc', 'itertools', 'marshal', 'math', 'mmap', 'msvcrt', 'nt', 'parser', 'sys', 'time', 'winreg', 'xxsubtype', 'zipimport', 'zlib')

    Сторонние ( англ. 3rd Party).

    Модули и пакеты, которые не входят в дистрибутив Python, и могут быть установлены из каталога пакетов Python ( англ. PyPI - the Python Package Index, более 90.000 пакетов) с помощью утилиты pip :

    C:\Users\yuri>pip install vk Collecting vk Downloading vk-2.0.2.tar.gz Requirement already satisfied (use --upgrade to upgrade): requests=2.8 in c:\users\yuri\appdata\local\programs\python\python35\lib\site-packages (from vk) Installing collected packages: vk Running setup.py install for vk … done Successfully installed vk-2.0.2

    При установке пакета автоматически устанавливаются зависимые пакеты.

    Пользовательские (собственные).

    Модули и пакеты, создаваемые разработчиком.

    Примечание

    Создание собственных пакетов не рассматривается в рамках настоящего курса.

В собственной программе рекомендуется выполнять импорт именно в таком порядке: от встроенных до собственных модулей/пакетов.

Как создать свой модуль в Python

Модулем python может быть любой программный файл python, который содержит код, включая функции, класс или переменные python. Другими словами, мы можем сказать, что файл кода Python, сохраненный с расширением(.py), рассматривается как модуль. У нас может быть исполняемый код внутри модуля python.

Модули в Python отличаются маневренностью в логической организации кода. Чтобы использовать функциональность одного модуля в другом, мы должны импортировать конкретный модуль.

Создадим модуль с именем file.py, который содержит функцию func, которая содержит код для вывода некоторого сообщения на консоль.

Пример:

#displayMsg prints a message to the name being passed. def displayMsg(name) print("Hi "+name);

Необходимо включить этот модуль в наш основной модуль, чтобы вызвать метод displayMsg(), определенный в модуле с именем file.

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

  1. Оператор импорта
  2. Оператор from-import

Оператор импорта

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

Мы можем импортировать несколько модулей с помощью одного оператора импорта, но модуль загружается один раз, независимо от того, сколько раз он был импортирован в наш файл.

Синтаксис для использования оператора импорта приведен ниже.

import module1,module2,…….. module n

Следовательно, если нам нужно вызвать функцию displayMsg(), определенную в файле file.py, мы должны импортировать этот файл как модуль в наш модуль, как показано в примере ниже.

Пример:

import file; name = input("Enter the name?") file.displayMsg(name)

Выход:

Enter the name?John Hi John

Оператор from-import

Вместо того, чтобы импортировать весь модуль, в python имеется возможность импортировать только определенные атрибутов модуля. Это можно сделать с помощью from-import оператора. Синтаксис для использования оператора from-import приведен ниже.

from import , ..,

Рассмотрим следующий модуль, называемый calculation, который содержит три функции: суммирование, умножение и деление.

calculation.py:

#place the code in the calculation.py def summation(a,b): return a+b def multiplication(a,b): return a*b; def divide(a,b): return a/b;

Main.py:

from calculation import summation #it will import only the summation() from calculation.py a = int(input("Enter the first number")) b = int(input("Enter the second number")) print("Sum = ",summation(a,b)) #we do not need to specify the module name while accessing summation()

Выход:

Enter the first number10 Enter the second number20 Sum = 30

Оператор from … import всегда лучше использовать, если мы заранее знаем атрибуты, которые нужно импортировать из модуля. Это не позволяет нашему коду быть тяжелее. Мы также можем импортировать все атрибуты из модуля, используя *.

Рассмотрим следующий синтаксис.

from import *

Переименование модуля

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

Синтаксис для переименования модуля приведен ниже.

import as

Пример:

#the module calculation of previous example is imported in this example as cal. import calculation as cal; a = int(input("Enter a?")); b = int(input("Enter b?")); print("Sum = ",cal.summation(a,b))

Выход:

Enter a?10 Enter b?20 Sum = 30

Использование функции dir()

Функция dir() возвращает отсортированный список имен, определенных в переданном модуле. Этот список содержит все подмодули, переменные и функции, определенные в этом модуле.

Рассмотрим следующий пример.

Что такое пакет в Python

Курс по Python: https://stepik.org/course/100707

На этом занятии речь пойдет о пакетах, узнаем, как их создавать и импортировать. Ранее мы с вами уже говорили о модулях – отдельных файлах с текстами программ, которые можно импортировать в другие программы. А пакет (package) – это специальным образом организованный подкаталог с набором модулей, как правило, решающих сходные задачи.

Давайте в качестве примера создадим пакет из модулей по обучающим курсам:

  • HTML
  • Java
  • PHP
  • Python

Это можно сделать, следующим образом. В PyCharm во вкладке «Project» щелкнуть правой кнопкой мыши и из выпадающего меню выбрать New -> Python Package. Здесь нам нужно придумать название пакета, пусть оно будет:

courses

Нажимаем Enter и пакет в рабочем каталоге создан. Посмотрим, что он из себя представляет. Переходим в рабочий каталог и видим в нем подкаталог с указанным именем courses. Внутри этого каталога находится только один пустой файл __init__.py. Чуть позже мы узнаем для чего он нужен.

Итак, пакет в Python – это обычный каталог, в котором обязательно должен располагаться специальный файл __init__.py.

Но пока наш пакет пустой, в нем нет ни одного модуля. Добавим их, то есть, добавим обычные python-файлы в этот каталог. Пусть они будут такими:

html.py:

java.py

php.py

python.py

И, кроме того, в файл __init__.py добавим строчку:

NAME = "package courses"

И обратите внимание. Для корректной обработки модулей в пакете, все файлы следует создавать с кодировкой UTF-8.

Все, мы сформировали пакет и теперь можем его импортировать в нашу программу. Для этого записывается ключевое слово import и указывается имя пакета (название подкаталога):

import courses

Давайте посмотрим, что в итоге было импортировано:

Мы видим имя нашей переменной NAME и вспомогательные переменные, которые были созданы автоматически средой Python:

Выведем значение переменной NAME:

И в консоли отображается строчка, прописанная в файле __init__.py. О чем это говорит? О том, что при импорте пакета файл __init__.py был выполнен. В действительности – это инициализатор пакета. В нем мы прописываем то, что следует импортировать при импорте пакета. Например, в нашем каталоге courses присутствуют четыре наших файла, но ни один из них не был использован в момент импорта. Это произошло, как раз, по той причине, что в файле __init__.py мы их никак не обрабатывали.

Чтобы получить доступ к функциям из модулей внутри пакета, их, в свою очередь, нужно импортировать в инициализаторе __init__.py, например, так:

import courses. python

Я, думаю, вы понимаете, почему вначале указан подкаталог courses? Так как модули находятся по нестандартному пути, то этот путь следует явно прописать. В данном случае, достаточно указать имя подкаталога, а затем, через точку имя файла.

Теперь, после запуска программы, мы видим, что в пространстве имен пакета courses появилось имя python. Поэтому, мы можем обратиться к этому модулю и вызвать его функцию:

Но, обычно, в инициализаторе пакетов импорт выполняют с помощью конструкции:

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

courses. get_python ( )

Но, все же, у такого способа импортирования модулей в инициализаторе пакета есть один существенный недостаток – мы, фактически, указываем полный путь к модулю (это называется абсолютным импортом ). Представьте, например, что в будущем, по каким-либо причинам, придется поменять название пакета. Тогда и все абсолютные импорты также придется переписывать. Поэтому здесь лучше воспользоваться относительным способом импортирования. Для этого, вместо имени основного подкаталога courses, ставится точка:

Эта точка означает «использовать текущий каталог». Так гораздо практичнее и мы теперь совершенно не привязаны к названию пакета.

Как создать свой пакет в Python

Предположим, вы хотите создать набор модулей для обработки музыкальных файлов. Взгляните на следующую структуру. Вот как вы организуете различные файлы в папке вашего пакета. В нашем случае папка пакета верхнего уровня – это «music»:

music/ Top-level package __init__.py Initialize the music package formats/ Subpackage for file conversions __init__.py wavread.py wavwrite.py aiffread.py aiffwrite.py auread.py auwrite.py … effects/ Subpackage for sound effects __init__.py echo.py surround.py reverse.py … filters/ Subpackage for filters __init__.py equalizer.py vocoder.py karaoke.py …

Каждый пакет в Python должен иметь файл __init__.py, который гарантирует, что этот каталог будет рассматриваться как пакет.

Как правило, __init__.py может быть просто пустым файлом или исполняемым кодом инициализации для пакета или задавать переменную __all__, которая будет рассмотрена в последней части этого руководства.

Импортировать отдельный модуль из пакета можно любым из следующих способов.

import music.formats.wavwrite

Или:

from music.formats import wavwrite

Приведенные выше операторы загружают подмодуль music.formats.wavwrite.

Что такое константа в Python

Давайте немного поговорим о константах в Питоне.

Во первых, давайте дадим понятие что есть константа в контексте компьютерного программирования?

Константой называется — такое значение объекта (величина), которое на протяжении работы программы не меняется.

А вот примеры констант из жизни:

— число Пи всегда равно 3.14159…,— количество минут в часх всегда равно 60,- масса протона в состоянии покоя всегда равна 930 МэВ,— код ASCII символа ESC всегда имеет значение равное 0x1B

ну и так далее.

Мы можем даже определять свои константы:

— количество колес на моем автомобиле всегда равно 4,— день рождения моей жены всегда 17 Апреля,- имя моей младшей дочери — «Светлана».

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

Во многих языках программирования принято писать имена констант в верхнем регистре. Эту традицию программистского мира, мне кажется, заложил язык Си. И люди от «сохи С/С++» свято придерживаются этой традиции.

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

Это всё преимущественно касается группы языков С.С++. А что можно сказать относительно Питона, ведь в нём нет такого понятия как «констната»?

В Питоне константой является сам объект, да и то не всякий!

Питон — это совершенно другой мир программирования, нежли традиционный C/C++/C#, Pascal. Basic,…

Например число 5 в Питоне — это в некотором смысле константа. Говоря языком Питона, число 5 — это неизменяемый объект. На этот объект могут ссылаться две переменные: alpha и beta

alpha = 5beta = 5

Здесь две переменные ссылаются на одно и то же число, на один и тот же объект. В контексте Python имя объекта — это есть ничто иное как ссылка на объект. В данном случае на объект-число 5.

Сами же объекты могут быть как изменяемые, так и неизменяемые. Неизменяемые объекты — это в некотором роде и есть константы. Число 5 никогда не может стать числом 6.

Иное дело, когда мы запишем:

alpha = 6

после этого alpha будет ссылаться (условно говоря — показывать пальцем) уже на другой неизменяемый объект. В Питоне нет встроенного механизма, который бы запрещал изменять «направления» ссылок — это противоречило бы духу Питона. Поэтому ссылки на изменяемые и неизменяемые объекты (то есть, говоря языком Си-шника — ссылки на константы и неконстанты) всегда могут быть изменены в любой момент времени.

Более того, ссылка на объект (в понимании Си-шника — имя переменной или константы) не типирована. Другими словами — если сейчас alpha указывает на число, то после исполнения следующего оператора alpha будет указывать на символьную строку:

alpha = ‘Мама, мама! Что мы будем делать?’

После чего мы можем направить alpha на какой-нибудь еще объект, например на более сложный:

alpha = open(‘my_data.text’, ‘r’)

Сейчас alpha ссылается на файловый объект, который открыт в режиме только для чтения.

Иначе говоря, если в Си мы напишем

#define ALPHA (125)

, а потом где-нибудь в тексте программы попытаемся переопределить ALPHA

ALPHA = 100;

, то компилятор отматерит нас, потому как ALPHA — это константа.

В Питоне же мы имеем полное право присваивать именам ссылки на любые объекты.

Тем не менее, проблема использования констант присутствует в любом языке программирования. Математическую константу ¶ (3.14159) никто не отменял.

Выделять имена констант большими буквами — это признак хорошего тона. По крайней мере это очень существенно в среде C/C++ и некоторых других языков программирования.

Однако, забавно отметить, что Python, очень часто «кладет» на эту Си-шную традицию.

Omega = 2.0 * math.pi * freq

Здесь константа «пи» написана строчными буквами. Более того, если вы откроете тексты программ, где используются внешние модули (например, для работы с графикой — Tkinter, wx,…), то увидите, что и тут многие константы также имеют имена, написанные строчными буквами.

Да. Вот такой он — Питон. Здесь всё необычно!

Так вот, ответ на вопрос — писать константы большими или маленьким  буквами зависит от соглашений, которые вы оговорили с коллегами, а не от самого языка программирования.

Как создать свою константу в Python

До этого момента вы узнали о константах как об общей концепции в жизни, науке и программировании. Теперь пришло время узнать, как Python работает с константами. Во-первых, вы должны знать, что в Python нет специального синтаксиса для определения констант.

Другими словами, в Python нет констант в строгом смысле этого слова. В нем есть только переменные, прежде всего из-за его динамической природы. Поэтому, чтобы иметь константу в Python, нужно определить переменную , которая никогда не меняется , и придерживаться этого поведения, избегая операций присваивания для самой переменной.

Примечание: В этом разделе вы сосредоточитесь на определении собственных констант. Однако есть несколько констант, которые встроены в Python. Вы узнаете о них.

Тогда как разработчики Python узнают, что данная переменная представляет собой константу? Сообщество Python решило использовать строгое соглашение об именовании для различения переменных и констант. Читайте дальше, чтобы узнать больше!

Определяемые пользователем константы

Чтобы сообщить другим программистам, что данное значение должно рассматриваться как константа , вы должны использовать общепринятое соглашение об именовании идентификатора или имени константы. Имя следует писать заглавными буквами с подчеркиванием, разделяющим слова, как указано в разделев PEP 8 .

Вот несколько примеров пользовательских констант Python:

PI = 3.14 MAX_SPEED = 300 DEFAULT_COLOR = "\033

Обратите внимание, что вы создали эти константы точно так же, как и переменные. Вы использовали описательное имя, оператор присваивания (=) и конкретное значение константы.

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

Что такое глобальная переменная в Python

Глобальные переменные в Python - это переменные, которые определены вне любой функции программы. К ним можно получить доступ и которые можно изменить из любой функции или модуля в программе.

Область видимости переменной в Python определяет ее доступность. В Python есть два типа областей видимости: Глобальная область и Локальная область. Глобальная область означает, что переменная доступна в течение всей программы, в то время как локальная область означает, что переменная доступна только внутри функции, где она определена.

Пример 1: Как Определить Глобальную Переменную в Python

# Define a global variable global_var = 10

В Python глобальные переменные можно получать и изменять из любой функции или модуля в программе. Однако присвоение значения глобальной переменной внутри функции создаёт новую локальную переменную в этой функции.

Вот некоторые примеры того, как работает область видимости глобальных переменных в Python:

Пример 2: Доступ к Глобальной Переменной Внутри Функции

x = 5 #global variable def func(): print(x) #accessing a global variable inside a function func() #calling the function # Output: 5

В этом примере функцияfuncполучает доступ к глобальной переменнойx, которая определена вне любой функции.

Пример 3: Доступ к Глобальной Переменной Вне Функции

x = 5 #global variable def func(): x = 10 #creating a new local variable print(x) #accessing the local variable inside the function func() print(x) #accessing the global variable outside the function # Output: 10 5

В этом примере функцияfuncсоздает новую локальную переменнуюx, присваивая ей значение10. Таким образом, операторprintв функции относится к локальной переменной, а не к глобальной. Однако, когда операторprintвызывается вне функции, он относится к глобальной переменнойx.

Как создать свою глобальную переменную в Python

Теперь разберемся с видимостью глобальных переменных между загружаемыми модулями Python. Например, мы подключаем другой модуль с помощью команды import. Создадим файл «test.py» и в него запишем следующий код:

print('Загружается модуль test') x = 100 def f(): print('Из функции x=' + str(x))

То есть мы определили глобальную переменную x для модуля test. Так же определили функцию, которая выводит на экран её значение.

Теперь создадим файл main.py, который и будем запускать. В нем мы импортируем модуль test, а так же создадим свою глобальную переменную x. После этого выведем значения глобальной переменной из test, вызовим функцию f, а так же проверим, что значение переменной в модуле main не изменилось:

x = 200 print('Из модуля main x=' + str(x)) import test print('Из модуля test x=' + str(test.x)) print('Присваиваем значение 300') test.x = 300; print('Из модуля test x=' + str(test.x)) test.f() print('Из модуля main x=' + str(x)) Из модуля main x=200 Загружается модуль test Из модуля test x=100 Присваиваем значение 300 Из модуля test x=300 Из функции x=300 Из модуля main x=200

Мы в первой же строчке записали в x значение 200. Это было сделано, чтобы показать, что после того, как мы загрузим внешний модуль, значение этой переменной не изменится. Так и вышло. Обращаясь к переменной из загруженной библиотеки, удалось прочитать его и изменить значение.

Теперь модифицируем программу следующим образом:

x = 200 print('Из модуля main x=' + str(x)) from test import * f() print('Из модуля main x=' + str(x)) print('Присваиваем значение 300') x = 300 f() print('Из модуля main x=' + str(x)) Из модуля main x=200 Загружается модуль test Из функции x=100 Из модуля main x=100 Присваиваем значение 300 Из функции x=100 Из модуля main x=300

В этом случае для загрузки мы использовали команду «from test import *». Мы импортировали все переменные и функции. После загрузки модуля значение переменной x в модуле main изменилось. Но при вызове функции, мы получаем значение x из модуля test. После присвоения нового значения переменной x, значение, которое выводит функция f не изменяется.

Следует по возможности избегать подключение библиотек с помощью команды from библиотека import * , а подключать только необходимые функции — from библиотека import функция. При этом надо удостовериться, что эти имена не используются в основном модуле.

Использование import библиотека поможет избежать возможных ошибок, если в программе есть функции, классы и переменные с такими же наименованиями, как и в загружаемом модуле.

Что такое декоратор в Python

Этот урок посвящен теме декораторов в . Большое внимание уделено свойствам функций в , на базе которых реализована идея декораторов. Рассмотрены декораторы принимающие аргументы и возвращающие значение из функции.

    Python ?

    Для начала разберем два аспекта связанные с функциями в Python . Во-первых: функция – это объект специального вида, поэтому ее можно передавать в качестве аргумента другим функциям. Во-вторых: внутри функций можно создавать другие функции, вызывать их и возвращать как результат через return . Остановимся на этих моментах более подробно.

    Функция как объект

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

    >>> # исходный список >>> a = >>> # функция, возводящая переданное ей число в квадрат >>> sq = lambda x: x**2 >>> # проверим ее работу >>> print(sq(5)) 25 >>> # получаем список квадратов >>> b = list(map(sq, a)) >>> print(b)

    Здесь мы передали функции map в качестве первого аргумента функцию sq , которая будет применяться по очереди ко всем элементам списка a .

    В Python функция – это специальный объект, который имеет метод __call__() . Если мы создадим вот такой класс.

    class DemoCall(): def __call__(self): return "Hello!"

    То объект такого класса можно вызывать как функцию.

    >>> hello = DemoCall() >>> hello() 'Hello!'

    Функция внутри функции

    Вторым важным свойством функции, для понимания темы декораторов, является то, что их можно создавать, вызывать и возвращать из других функций. На этом построена идея замыкания ( closures ).

    Например, создадим функцию, которая умножает два числа.

    def mul(a): def helper(b): return a * b return helper

    Вызывается эта функция так:

    >>>mul(4)(2) 8

    Ее главная фишка состоит в том, что можно создавать на базе функции mul() свои кастомизированные функции. Например, создадим функцию “умножение на три”.

    >>>three_mul = mul(3) >>>three_mul(5) 15

    Как вы можете видеть, мы построили функцию three_mul , которая умножает на три любое переданное ей число.

    Что такое декоратор функции в Python ?

    Конструктивно декоратор в Python представляет собой некоторую функцию, аргументом которой является другая функция. Декоратор предназначен для добавления дополнительного функционала к данной функции без изменения содержимого последней.

    Создание декоратора

    Предположим у нас есть пара простых функций, вот они:

    def first_test(): print("Test function 1") def second_test(): print("Test function 2")

    Мы хотим дополнить их так, чтобы перед вызовом основного кода функции печаталась строка “Run function” , а по окончании – “Stop function” .

    Сделать это можно двумя способами. Первый – это добавить указанные строки в начало в конец каждой функции, но это не очень удобно, т.к. если мы захотим убрать это, нам придется снова модифицировать тело функции. А если они написаны не нами, либо являются частью общей кодовой базы проекта, сделать это будет уже не так просто. Второй вариант – это воспользоваться знаниями из раздела “Что нужно знать о функциях в Python ?”

    Создадим вот такую функцию.

    def simple_decore(fn): def wrapper(): print("Run function") fn() print("Stop function") return wrapper

    Обернем наши функции в эту оболочку.

    first_test_wrapped = simple_decore(first_test) second_test_wrapped = simple_decore(second_test)

    >>> first_test() Test function 1 >>> second_test() Test function 2

    >>> first_test_wrapped() Run function Test function 1 Stop function >>> first_test_wrapped() Run function Test function 1 Stop function

    first_test = first_test_wrapped second_test = second_test_wrapped

    Проверим это.

    >>> first_test() Run function Test function 1 Stop function >>> second_test() Run function Test function 2 Stop function

    То, что мы только что сделали и является реализацией идеи декоратора. Но вместо строк:

    def first_test(): print("Test function 1") first_test_wrapped = simple_decore(first_test) first_test = first_test_wrapped

    Можно написать вот так:

    @simple_decore def first_test(): print("Test function 1")

    @simple_decore – это и есть декоратор функции.

    Как создать свой декоратор в Python

    Лучший способ продемонстрировать, каковы декораторы в действии,  —  создать и задействовать один из них. Реализуем наш декоратор

    Вначале у нас есть только этот метод, который делит два числа:

    def divide(x, y): return x / y

    Проблема с этим методом в том, что нет проверки, не равно ли0значениеy. Очевидное решение здесь  —  задействовать простую проверкуif. Но есть и альтернативное решение: декораторы .

    Начнем с создания простой функции-декоратора guard_zero:

    Декоратор в Python  —  это такая же обычная функция. Она принимает в качестве аргумента функцию operate. Затем расширяет функциональностьoperate, создавая внутреннюю функцию и добавляя туда расширенное поведение . После чего возвращает внутреннюю функциюinner, которая становится новой версией функцииoperate.

    И вот декораторguard_zeroготов. Теперь расширим (т. е. декорируем) функциюdivide:

    divide = guard_zero(divide)

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

    Есть и более распространенный синтаксис применения декоратора. Если в предыдущем синтаксисе функция заменялась новой своей версией, то здесь декоратор просто ставится перед определением функции:

    @guard_zero def divide(x, y): return x / y

    Такой синтаксис более удобен для восприятия человеком, и суть его ясна: метод divide расширяется, чтобы исключить деление на 0 .

    Теперь пора протестировать методdivideразличными входными данными и убедиться, что декоратор делает то, что должен:

    print(divide(5, 0)) print(divide(5, 2))

    Вывод:

    Обратите внимание наNoneв выводе. А все потому, что декораторguard_zeroвозвращаетNone, когда значениеyравно0.

    Вот и все! Теперь вы знаете, что такое декораторы и как их использовать. А вот и весь код:

    def guard_zero(operate): def inner(x, y): if y == 0: print("Cannot divide by 0.") return return operate(x, y) return inner @guard_zero def divide(x, y): return x / y print(divide(5, 0)) # выводит Cannot divide by 0 («На 0 делить нельзя»)

    Напомним: метод декоратораguard_zeroпринимает в качестве аргумента функциюdivideи создает ее расширенную версию.

    Что такое метакласс в Python

    Итак, в Python все является объектом: числа, строки, булевы значения, списки, словари и т.д. Все это объекты в языке Python. Эти объекты созданы, образованы от соответствующих классов или типов данных: int, str, bool, list, dict и т.д. Понятия класс и тип данных, по сути, являются синонимами в Python. Но классы тоже являются объектами. То есть класс объект для создания объектов. Поэтому есть что-то, что может создавать классы. И это метакласс. Метакласс тоже объект (и, в свою очередь, класс). Его нельзя создать другим метаклассом. Он находится на вершине.

    Здесь можно провести следующую аналогию: Есть какое-то оборудование: инструменты, машины, станки – в общем средства для производства материальных благ. Но есть то, что их производит – средства для производства средств производства – машиностроение, станкостроение или тяжелая промышленность. Конкретное значение, например, целое число 3 – материальное благо, класс – средство для производства этих материальных благ, а метаклассы –средства для производства средств производства.

    Метакласс это то, что может создавать классы. Можно сказать, что метакласс создает объект-класс по созданию объектов-экземпляров класса. В Python метаклассом является объект type. Это также одноименная функция для определения типа объекта. Но функция type имеет другое применение: она также может создавать классы динамически. То есть одна и та же функция может использоваться для двух совершенно разных вещей в зависимости от передаваемых аргументов. Семантика у type будет совершенно разной в зависимости от того, сколько аргументов вы ему сообщите 1 или 3. Это сделано по историческим причинам, для сохранения обратной совместимости. type принимает на вход описание класса (имя класса, кортеж родительских классов и словарь с атрибутами) и возвращает динамически новый класс, новый тип данных.

    type(obj) – определить тип

    type(name, bases, dict) – создать тип

    Один аргумент:

    obj : Объект, тип которого требуется определить.

    Три аргумента:

    name : Имя для создаваемого типа (становится атрибутом __name__);

    bases : Кортеж с родительскими классами (становится атрибутом __bases__);

    dict : Словарь, который будет являться пространством имён для тела класса (становится атрибутом __dict__).

    На самом деле все стандартные типы данных порождены метаклассом type.

    >>> type(3) # Целое число порождено классом целых чисел >>> >>> type(int) # Класс целых чисел, в свою очередь, порожден метаклассом type >>> >>> class A: pass # Пользовательские классы также создаются этим метаклассом >>> type(A) >>> >>> type(type) # И даже метакласс type как бы порождает сам себя

    То, что type является метаклассом для самого себя, есть уловка на уровне реализации. Он является встроенным метаклассом в ядре языка и не создается каким-либо другим классом или метаклассом.