Главная страница

ээдд. Прохоренок_Н_А__Дронов_В_А_Python_3_и_PyQt_5_Разработка_приложен. Николай Прохоренок Владимир Дронов


Скачать 7.92 Mb.
НазваниеНиколай Прохоренок Владимир Дронов
Дата05.05.2023
Размер7.92 Mb.
Формат файлаpdf
Имя файлаПрохоренок_Н_А__Дронов_В_А_Python_3_и_PyQt_5_Разработка_приложен.pdf
ТипДокументы
#1111379
страница22 из 83
1   ...   18   19   20   21   22   23   24   25   ...   83
Глобальные идентификаторы вне функции
['__annotations__', '__builtins__', '__doc__', '__file__', '__loader__',
'__name__', '__package__', '__spec__', 'func', 'glob1', 'glob2']
 vars([<Объект>])
— если вызывается без параметра внутри функции, возвращает сло- варь с локальными идентификаторами. Если вызывается без параметра вне функции, возвращает словарь с глобальными идентификаторами. При указании объекта в качестве параметра возвращает идентификаторы этого объекта (эквивалентно вызову
<Объект>.__ dict__
). Пример использования этой функции можно увидеть в листинге 11.35.
Листинг 11.35. Использование функции vars() def func(): local1 = 54 glob2 = 25 print("Локальные идентификаторы внутри функции") print(sorted(vars().keys())) glob1, glob2 = 10, 88 func() print("Глобальные идентификаторы вне функции") print(sorted(vars().keys())) print("Указание объекта в качестве параметра") print(sorted(vars(dict).keys())) print("Альтернативный вызов") print(sorted(dict.__dict__.keys()))

228
Часть I. Основы языка Python
11.10. Вложенные функции
Одну функцию можно вложить в другую функцию, причем уровень вложенности не огра- ничен. При этом вложенная функция получает свою собственную локальную область види- мости и имеет доступ к переменным, объявленным внутри функции, в которую она вложена
(функции-родителя). Рассмотрим вложение функций на примере (листинг 11.36).
Листинг 11.36. Вложенные функции def func1(x): def func2(): print(x) return func2 f1 = func1(10) f2 = func1(99) f1() # Выведет: 10 f2() # Выведет: 99
Здесь мы определили функцию func1()
, принимающую один параметр, а внутри нее — вложенную функцию func2()
. Результатом выполнения функции func1()
будет ссылка на эту вложенную функцию. Внутри функции func2()
мы производим вывод значения пере- менной x
, которая является локальной в функции func1()
. Таким образом, помимо локаль- ной, глобальной и встроенной областей видимости, добавляется вложенная область види- мости. При этом поиск идентификаторов вначале производится внутри вложенной функ- ции, затем внутри функции-родителя, далее в функциях более высокого уровня и лишь потом в глобальной и встроенной областях видимости. В нашем примере переменная x
бу- дет найдена в области видимости функции func1()
Следует учитывать, что в момент определения функции сохраняются ссылки на перемен- ные, а не их значения. Например, если после определения функции func2()
произвести из- менение переменной x
, то будет использоваться это новое значение (листинг 11.37).
Листинг 11.37. При объявлении вложенной функции сохраняется ссылка на переменную def func1(x): def func2(): print(x) x = 30 return func2 f1 = func1(10) f2 = func1(99) f1() # Выведет: 30 f2() # Выведет: 30
Обратите внимание на результат выполнения. В обоих случаях мы получили значение
30
Если необходимо сохранить именно значение переменной при определении вложенной функции, следует передать значение как значение по умолчанию (листинг 11.38).

Глава 11. Пользовательские функции
229
Листинг 11.38. Принудительное сохранение значения переменной def func1(x): def func2(x=x): # Сохраняем текущее значение, а не ссылку print(x) x = 30 return func2 f1 = func1(10) f2 = func1(99) f1() # Выведет: 10 f2() # Выведет: 99
Теперь попробуем из вложенной функции func2()
изменить значение переменной x
, объяв- ленной внутри функции func1()
. Если внутри функции func2()
присвоить значение пере- менной x
, будет создана новая локальная переменная с таким же именем. Если внутри функции func2()
объявить переменную как глобальную и присвоить ей значение, то изме- нится значение глобальной переменной, а не значение переменной x
внутри функции func1()
. Таким образом, ни один из изученных ранее способов не позволяет из вложенной функции изменить значение переменной, объявленной внутри функции-родителя. Чтобы решить эту проблему, следует объявить необходимые переменные с помощью ключевого слова nonlocal
(листинг 11.39).
Листинг 11.39. Ключевое слово nonlocal def func1(a): x = a def func2(b): nonlocal x # Объявляем переменную как nonlocal print(x) x = b # Можем изменить значение x в func1() return func2 f = func1(10) f(5) # Выведет: 10 f(12) # Выведет: 5 f(3) # Выведет: 12
При использовании ключевого слова nonlocal следует помнить, что переменная обязатель- но должна существовать внутри функции-родителя. В противном случае будет выведено сообщение об ошибке.
11.11. Аннотации функций
В Python функции могут содержать аннотации, которые вводят новый способ документи- рования. Теперь в заголовке функции допускается указывать предназначение каждого параметра, его тип данных, а также тип возвращаемого функцией значения. Аннотации имеют следующий формат:

230
Часть I. Основы языка Python def <Имя функции>(
[<Параметр1>[: <Выражение>] [= <Значение по умолчанию>][, ...,
<ПараметрN>[: <Выражение>] [= <Значение по умолчанию>]]]
) -> <Возвращаемое значение>:
<Тело функции>
В параметрах
<Выражение>
и
<Возвращаемое значение>
можно указать любое допустимое выражение языка Python. Это выражение будет выполнено при создании функции.
Пример указания аннотаций:
>>> def func(a: "Параметр1", b: 10 + 5 = 3) -> None: print(a, b)
В этом примере для переменной a
создано описание "Параметр1"
. Для переменной b
выра- жение
10 + 5
является описанием, а число
3
— значением параметра по умолчанию. Кроме того, после закрывающей круглой скобки указан тип возвращаемого функцией значения:
None
. После создания функции все выражения будут выполнены, и результаты сохранятся в виде словаря в атрибуте
__annotations__
объекта функции.
Для примера выведем значение этого атрибута:
>>> def func(a: "Параметр1", b: 10 + 5 = 3) -> None: pass
>>> func.__annotations__
{'a': 'Параметр1', 'b': 15, 'return': None}

ГЛ А В А
12
Модули и пакеты
Модулем в языке Python называется любой файл с программным кодом. Каждый модуль может импортировать другой модуль, получая таким образом доступ к атрибутам (пере- менным, функциям и классам), объявленным внутри импортированного модуля. Следует заметить, что импортируемый модуль может содержать программу не только на Python — можно импортировать скомпилированный модуль, написанный на языке C.
Все программы, которые мы запускали ранее, были расположены в модуле с названием "__main__"
. Получить имя модуля позволяет предопределенный атрибут
__name__
. Для за- пускаемого модуля он содержит значение "__main__"
, а для импортируемого модуля — его имя. Выведем название модуля: print(__name__) # Выведет: __main__
Проверить, является модуль главной программой или импортированным модулем, позволя- ет код, приведенный в листинге 12.1.
Листинг 12.1. Проверка способа запуска модуля if __name__ == "__main__": print("Это главная программа") else: print("Импортированный модуль")
12.1. Инструкция import
Импортировать модуль позволяет инструкция import
. Мы уже не раз обращались к этой инструкции для подключения встроенных модулей. Например, подключали модуль time для получения текущей даты с помощью функции strftime()
: import time # Импортируем модуль time print(time.strftime("%d.%m.%Y")) # Выводим текущую дату
Инструкция import имеет следующий формат: import <Название модуля 1> [as <Псевдоним 1>][, ...,
<Название модуля N> [as <Псевдоним N>]]
После ключевого слова import указывается название модуля. Обратите внимание на то, что название не должно содержать расширения и пути к файлу. При именовании модулей необ-

232
Часть I. Основы языка Python ходимо учитывать, что операция импорта создает одноименный идентификатор, — это означает, что название модуля должно полностью соответствовать правилам именования переменных. Можно создать модуль с именем, начинающимся с цифры, но импортировать такой модуль нельзя. Кроме того, следует избегать совпадения имен модулей с ключевыми словами, встроенными идентификаторами и названиями модулей, входящих в стандартную библиотеку.
За один раз можно импортировать сразу несколько модулей, записав их через запятую. Для примера подключим модули time и math
(листинг 12.2).
Листинг 12.2. Подключение нескольких модулей сразу import time, math # Импортируем несколько модулей сразу print(time.strftime("%d.%m.%Y")) # Текущая дата print(math.pi) # Число pi
После импортирования модуля его название становится идентификатором, через который можно получить доступ к атрибутам, определенным внутри модуля. Доступ к атрибутам модуля осуществляется с помощью точечной нотации. Например, обратиться к константе pi
, расположенной внутри модуля math
, можно так: math.pi
Функция getattr()
позволяет получить значение атрибута модуля по его названию, задан- ному в виде строки. С помощью этой функции можно сформировать название атрибута динамически во время выполнения программы. Формат функции: getattr(<Объект модуля>, <Атрибут>[, <Значение по умолчанию>])
Если указанный атрибут не найден, возбуждается исключение
AttributeError
. Чтобы избе- жать вывода сообщения об ошибке, в третьем параметре можно указать значение, которое будет возвращаться, если атрибут не существует. Пример использования функции приведен в листинге 12.3.
Листинг 12.3. Пример использования функции getattr() import math print(getattr(math, "pi")) # Число pi print(getattr(math, "x", 50)) # Число 50, т. к. x не существует
Проверить существование атрибута позволяет функция hasattr(<Объект>, <Название атри- бута>)
. Если атрибут существует, функция возвращает значение
True
. Напишем функцию проверки существования атрибута в модуле math
(листинг 12.4).
Листинг 12.4. Проверка существования атрибута import math def hasattr_math(attr): if hasattr(math, attr): return "Атрибут существует" else: return "Атрибут не существует" print(hasattr_math("pi")) # Атрибут существует print(hasattr_math("x")) # Атрибут не существует

Глава 12. Модули и пакеты
233
Если название модуля слишком длинное и его неудобно указывать каждый раз для доступа к атрибутам модуля, то можно создать псевдоним. Псевдоним задается после ключевого слова as
. Создадим псевдоним для модуля math
(листинг 12.5).
Листинг 12.5. Использование псевдонимов import math as m # Создание псевдонима print(m.pi) # Число pi
Теперь доступ к атрибутам модуля math может осуществляться только с помощью иденти- фикатора m
. Идентификатор math в этом случае использовать уже нельзя.
Все содержимое импортированного модуля доступно только через название или псевдоним, указанный в инструкции import
. Это означает, что любая глобальная переменная на самом деле является глобальной переменной модуля. По этой причине модули часто используются как пространства имен. Для примера создадим модуль под названием tests.py
, в котором определим переменную x
(листинг 12.6).
Листинг 12.6. Содержимое модуля tests.py
# -*- coding: utf-8 -*- x = 50
В основной программе также определим переменную x
, но с другим значением. Затем под- ключим файл tests.py и выведем значения переменных (листинг 12.7).
Листинг 12.7. Содержимое основной программы
# -*- coding: utf-8 -*- import tests # Подключаем файл tests.py x = 22 print(tests.x) # Значение переменной x внутри модуля print(x) # Значение переменной x в основной программе input()
Оба файла размещаем в одной папке, а затем запускаем файл с основной программой с по- мощью двойного щелчка на значке файла. Как видно из результата, никакого конфликта имен нет, поскольку одноименные переменные расположены в разных пространствах имен.
Как говорилось еще в главе 1, перед собственно выполнением каждый модуль Python ком- пилируется, преобразуясь в особое внутреннее представление (байт-код), — это делается для ускорения выполнения кода. Файлы с откомпилированным кодом хранятся в папке
__pycache__
, автоматически создающейся в папке, где находится сам файл с исходным, неоткомпилированным кодом модуля, и имеют имена вида
<имя файла с исходным ко- дом>.cpython-<первые две цифры номера версии Python>.pyc
. Так, при запуске на исполне- ние нашего файла tests.py откомпилированный код будет сохранен в файле tests.cpython-
36.pyc
Следует заметить, что для импортирования модуля достаточно иметь только файл с отком- пилированным кодом, файл с исходным кодом в этом случае не нужен. Для примера пере- именуйте файл tests.py
(например, в tests1.py
), скопируйте файл tests.cpython-36.pyc из пап- ки
__pycache__
в папку с основной программой и переименуйте его в tests.pyc
, а затем за-

234
Часть I. Основы языка Python пустите основную программу. Программа будет нормально выполняться. Таким образом, чтобы скрыть исходный код модулей, можно поставлять клиентам программу только с фай- лами, имеющими расширение pyc
Существует еще одно обстоятельство, на которое следует обратить внимание. Импортиро- вание модуля выполняется только при первом вызове инструкции import
(или from
, речь о которой пойдет позже). При каждом вызове инструкции import проверяется наличие объекта модуля в словаре modules из модуля sys
. Если ссылка на модуль находится в этом словаре, то модуль повторно импортироваться не будет. Для примера выведем ключи сло- варя modules
, предварительно отсортировав их (листинг 12.8).
Листинг 12.8. Вывод ключей словаря modules
# -*- coding: utf-8 -*- import tests, sys # Подключаем модули tests и sys print(sorted(sys.modules.keys())) input()
Инструкция import требует явного указания объекта модуля. Так, нельзя передать название модуля в виде строки. Чтобы подключить модуль, название которого формируется про- граммно, следует воспользоваться функцией
__import__()
. Для примера подключим модуль tests py с помощью функции
__import__()
(листинг 12.9).
Листинг 12.9. Использование функции __import__()
# -*- coding: utf-8 -*- s = "test" + "s" # Динамическое создание названия модуля m = __import__(s) # Подключение модуля tests print(m.x) # Вывод значения атрибута x input()
Получить список всех идентификаторов внутри модуля позволяет функция dir()
. Кроме того, можно воспользоваться словарем
__dict__
, который содержит все идентификаторы и их значения (листинг 12.10).
Листинг 12.10. Вывод списка всех идентификаторов
# -*- coding: utf-8 -*- import tests print(dir(tests)) print(sorted(tests.__dict__.keys())) input()
12.2. Инструкция from
Для импортирования только определенных идентификаторов из модуля можно воспользо- ваться инструкцией from
. Ее формат таков: from <Название модуля> import <Идентификатор 1> [as <Псевдоним 1>]
[, ..., <Идентификатор N> [as <Псевдоним N>]]

Глава 12. Модули и пакеты
235 from <Название модуля> import (<Идентификатор 1> [as <Псевдоним 1>],
[..., <Идентификатор N> [as <Псевдоним N>]]) from <Название модуля> import *
Первые два варианта позволяют импортировать модуль и сделать доступными только ука- занные идентификаторы. Для длинных имен можно назначить псевдонимы, указав их после ключевого слова as
. В качестве примера сделаем доступными константу pi и функцию floor()
из модуля math
, а для названия функции создадим псевдоним (листинг 12.11).
Листинг 12.11. Инструкция from
# -*- coding: utf-8 -*- from math import pi, floor as f print(pi) # Вывод числа pi
# Вызываем функцию floor() через идентификатор f print(f(5.49)) # Выведет: 5 input()
Идентификаторы можно разместить на нескольких строках, указав их названия через запя- тую внутри круглых скобок: from math import (pi, floor, sin, cos)
Третий вариант формата инструкции from позволяет импортировать из модуля все иденти- фикаторы. Для примера импортируем все идентификаторы из модуля math
(листинг 12.12).
Листинг 12.12. Импорт всех идентификаторов из модуля
# -*- coding: utf-8 -*- from math import * # Импортируем все идентификаторы из модуля math print(pi) # Вывод числа pi print(floor(5.49)) # Вызываем функцию floor() input()
Следует заметить, что идентификаторы, названия которых начинаются с символа подчерки- вания, импортированы не будут. Кроме того, необходимо учитывать, что импортирование всех идентификаторов из модуля может нарушить пространство имен главной программы, т. к. идентификаторы, имеющие одинаковые имена, будут перезаписаны.
Создадим два модуля и подключим их с помощью инструкций from и import
. Содержимое файла module1.py приведено в листинге 12.13.
Листинг 12.13. Содержимое файла module1.py
# -*- coding: utf-8 -*- s = "Значение из модуля module1"
Содержимое файла module2.py приведено в листинге 12.14.
Листинг 12.14. Содержимое файла module2.py
# -*- coding: utf-8 -*- s = "Значение из модуля module2"

236
Часть I. Основы языка Python
Исходный код основной программы приведен в листинге 12.15.
Листинг 12.15. Код основной программы
# -*- coding: utf-8 -*- from module1 import * from module2 import * import module1, module2 print(s) # Выведет: "Значение из модуля module2" print(module1.s) # Выведет: "Значение из модуля module1" print(module2.s) # Выведет: "Значение из модуля module2" input()
Итак, в обоих модулях определена переменная с именем s
. Размещаем все файлы в одной папке, а затем запускаем основную программу с помощью двойного щелчка на значке фай- ла. При импортировании всех идентификаторов значением переменной s
станет значение из модуля, который был импортирован последним, — в нашем случае это значение из модуля module2.py
. Получить доступ к обеим переменным можно только при использовании инст- рукции import
. Благодаря точечной нотации пространство имен не нарушается.
В атрибуте
__all__
можно указать список идентификаторов, которые будут импортировать- ся с помощью выражения from module import *
. Идентификаторы внутри списка указыва- ются в виде строки. Создадим файл module3.py
(листинг 12.16).
Листинг 12.16. Использование атрибута __all__
# -*- coding: utf-8 -*- x, y, z, _s = 10, 80, 22, "Строка"
1   ...   18   19   20   21   22   23   24   25   ...   83


написать администратору сайта