Разделение кода на модули и пакеты
- Разделение кода на модули и пакеты
- Связанные вопросы и ответы
- Что такое модуль в Python
- Как создать свой модуль в Python
- Что такое пакет в Python
- Как создать свой пакет в Python
- Что такое константа в Python
- Как создать свою константу в Python
- Что такое глобальная переменная в Python
- Как создать свою глобальную переменную в Python
- Что такое декоратор в Python
- Как создать свой декоратор в Python
- Что такое метакласс в Python
Разделение кода на модули и пакеты
В книге “Совершенный Код” Стив Макконнелл формулирует главный технический императив программирования – это управление сложностью . Основная суть, которого заключается в том, что на каждом этапе разработки ПО мы должны прикладывать максимум усилий для того чтобы сложность нашего проекта не “вышла из берегов”. Показателем этого является возможность одновременно удержать в голове основные компоненты проекта на всех уровнях абстракции. В моделировании систем (да и не только там) выделят такой инструмент как декомпозиция – разделение целого на части, этот принцип является одним из наиболее часто используемых способов работать со сложностью. Декомпозицию можно делать на логическом и на физическом уровне. Для реализации последней цели (декомпозиция на физическом уровне) в программном проекте на Python могут служить модули и пакеты.
Для простоты понимания, того, что будет изложено далее, под термином модуль в Python мы будем понимать файл с исходным кодом, имеющий расширение .py , а пакет – это каталог, который может включать другие каталоги или модули.
Модуль может содержать функции (например, модуль с тригонометрическими функциями), классы, объекты классов и переменные – все это будет доступно для других модулей, если правильно импортировать данный. Также в модуле может содержаться программный код, который будет выполняться, если скрипт запустить на исполнение.
Следует заметить, что вы можете импортировать как весь модуль целиком, так и избранные его части.
Разделение кода на модули и пакеты
В книге "Совершенный Код" Стив Макконнелл формулирует главный технический императив программирования – это управление сложностью. Основная суть, которого заключается в том, что на каждом этапе разработки ПО мы должны прикладывать максимум усилий для того, чтобы сложность нашего проекта не "вышла из берегов". Показателем этого является возможность одновременно удержать в голове основные компоненты проекта на всех уровнях абстракции. В моделировании систем (да и не только там) выделяют такой инструмент как декомпозиция – разделение целого на части, этот принцип является одним из наиболее часто используемых способов работы со сложностью. Декомпозицию можно делать на логическом и на физическом уровне. Для реализации последней цели (декомпозиция на физическом уровне) в программном проекте на 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 предоставляет два типа операторов:
- Оператор импорта
- Оператор 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
Рассмотрим следующий модуль, называемый 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
Переименование модуля
Python предоставляет нам возможность импорта некоторого модуля с определенным именем, чтобы мы могли использовать его имя для этого модуля в нашем исходном файле python.
Синтаксис для переименования модуля приведен ниже.
import
Пример:
#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 не изменяется.
Что такое декоратор в 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 : Имя для создаваемого типа (становится атрибутом bases : Кортеж с родительскими классами (становится атрибутом dict : Словарь, который будет являться пространством имён для тела класса (становится атрибутом |
На самом деле все стандартные типы данных порождены метаклассом type.
>>> type(3) # Целое число порождено классом целых чисел
То, что type является метаклассом для самого себя, есть уловка на уровне реализации. Он является встроенным метаклассом в ядре языка и не создается каким-либо другим классом или метаклассом.