Основы программирования на Python Каширский / методички / Лабораторная работа № 5 2025 (2)
.pdf
Дополнительный материал по функциям
Замыкания
Замыкание (closure) представляет функцию, которая запоминает свое лексическое окружение даже в том случае, когда она выполняется вне своей области видимости.
Технически замыкание включает три компонента:
•внешняя функция, которая определяет некоторую область видимости и в которой определены некоторые переменные и параметры - лексическое окружение
•переменные и параметры (лексическое окружение), которые определены во внешней функции
•вложенная функция, которая использует переменные и параметры внешней функции
Для определения замыканий в Python применяются локальные функции:
def outer(): # внешняя функция
n = 5 # лексическое окружение - локальная переменная
def inner(): # локальная функция nonlocal n
n += 1 # операции с лексическим окружением print(n)
return inner
fn = outer() # fn = inner, так как функция outer возвращает функцию inner
# вызываем внутреннюю функцию inner fn() # 6
fn() # 7 fn() # 8
Здесь функция outer определяет локальную переменную n - это и есть лексическое окружение для внутренней функции.
Внутри функции outer определена внутренняя функция - локальная функция inner, которая обращается к своему лексическому окружению - переменной n - увеличивает ее значение на единицу и выводит на консоль. Эта локальная функция возвращается функцией outer.
В программе вызываем функцию outer и получаем в переменную fn локальную функцию inner.
Переменная fn и представляет собой замыкание, то есть объединяет две вещи: функцию и окружение, в котором функция была создана. И несмотря на то, что мы получили локальную функцию и можем ее вызывать вне ее окружающей функции, в которой она определена, тем не менее она запомнила свое лексическое окружение и может к нему обращаться и изменять, что мы увидим по консольному выводу.
Кроме внешних переменных к лексическому окружению также относятся параметры окружающей функции. Рассмотрим использование параметров
def multiply(n):
def inner(m): return n * m
return inner
fn = multiply(5) |
|
|
|
|
|
print(fn(5)) |
# 25 |
|
print(fn(6)) |
# |
30 |
print(fn(7)) |
# |
35 |
Здесь внешняя функция - multiply возвращает функцию, которая принимает число и возвращает число.
Вызов функции multiply() возвращает локальную функцию inner.
Эта функция запоминает окружение, в котором она была создана, в частности, значение параметра n. Кроме того, сама принимает параметр и возвращает произведение параметров n и m.
В итоге при вызове функции multiply определяется переменная fn, которая получает локальную функцию inner и ее лексическое окружение - значение параметра n. В данном случае параметр n равен 5.
При вызове локальной функции, например число 6 передается для параметра m локальной функции, которая возвращает произведение n и m, то есть 5 * 6 = 30.
Декораторы
Декораторы в Python представляют функцию, которая в качестве параметра получает функцию и в качестве результата также возвращает функцию. Декораторы позволяют модифицировать выполняемую функцию, значения ее параметров и ее результат без изменения исходного кода этой функции.
Рассмотрим простейший пример:
|
|
|
|
|
|
|
# определение функции декоратора |
|
|
|
|
|
def select(input_func): |
|
|
|
|
|
def output_func(): |
# определяем функцию, которая |
|
||
|
будет выполняться вместо оригинальной |
|
|
||
|
|
|
|
|
|
|
print("*****************") |
# |
перед выводом |
|
|
|
оригинальной функции выводим звездочки |
|
|||
|
input_func() |
|
# |
вызов оригинальной |
|
|
функции |
|
|
|
|
|
|
|
|
|
|
print("*****************") # после вывода оригинальной функции выводим звездочки
return output_func # возвращаем новую функцию
# определение оригинальной функции
@select # применение декоратора select def hello():
print("Hello")
# вызов оригинальной функции hello()
Вначале определяется собственно функция декоратора, которая в данном случае называется select(). В качестве параметра декоратор получает функцию (в данном случае параметр input_func), к которой этот декоратор будет применяться.
Результатом декоратора в данном случае является локальная функция output_func, в которой вызывается входная функция input_func. Для простоты здесь перед и после вызыва input_func для красоты просто выводим набор символов "*".
Далее определяется стандартная функция, к которой применяется декоратор - в данном случае это функция hello, которая просто выводит на консоль некоторую строку
Для применения декоратора перед определением функции указывается символ @, после которого идет имя декоратора. То есть в данном случае к функции hello() применяется декоратор select().
Далее вызываем функцию hello. Поскольку к этой функции применяется декоратор select, то в результате функциия hello передается в декоратор select() в качестве параметра input_func. И поскольку декоратор возвращает новую функцию - output_func, то фактически в данном случае будет выполняться именно эта функция output_func()
В итоге мы получим следующий консольный вывод:
*****************
Hello
*****************
Декоратор может перехватывать передаваемые в функцию аргументы:
# определение функции декоратора
def check(input_func): |
|
|
|
def output_func(*args): |
# через *args получаем |
значения параметров оригинальной функции |
|
input_func(*args) |
# вызов оригинальной |
функции |
|
return output_func |
# возвращаем новую функцию |
|
|
#определение оригинальной функции
@check
def print_person(name, age): print(f"Name: {name} Age: {age}")
#вызов оригинальной функции
print_person("Tom", 38)
Здесь функция print_person() принимает два параметра: name (имя) и age (возраст). К этой функции применяется декоратор check().
Вдекораторе check возвращается локальная функция output_func(), которая принимает некоторый набор значений в виде параметра *args - это те значения, которые передаются в оригинальную функцию, к которой применяется декоратор. То есть в данном случае *args будет содержать значения параметров name и age.
Витоге в данном получим следующий консольный вывод
Name: Tom Age: 38
Но что, если в функцию print_person будет передано какое-то недопустимое значение, например, отрицательный возраст? Одним из преимуществ декораторов как раз является то, что мы можем проверить и при необходимости модифицировать значения параметров. Например:
# определение функции декоратора |
|
def check(input_func): |
|
def output_func(*args): |
|
name = args[0] |
|
age = args[1] |
# получаем значение второго |
параметра |
|
if age < 0: age = 1 |
# если возраст отрицательный, |
изменяем его значение на 1 |
|
input_func(name, age) |
# передаем функции значения |
для параметров |
|
return output_func |
|
|
|
#определение оригинальной функции
@check
def print_person(name, age): print(f"Name: {name} Age: {age}")
#вызов оригинальной функции
print_person("Tom", 38) print_person("Bob", -5)
args фактически представляет набор значений, и, используя индексы, мы можем получить значения параметров по позиции и что-то с ними сделать. Так, здесь, если значение возраста меньше 0, то устанавливаем 1. Затем передаем эти значения в вызов функции. В итоге здесь получим следующий вывод:
Name: Tom Age: 38
Name: Bob Age: 1
Подобным образом можно получить результат функции и при необходимости изменить его:
|
# определение функции декоратора |
|
|
|
|
|
|
|
def check(input_func): |
|
|
|
def output_func(*args): |
|
|
|
result = input_func(*args) |
# передаем функции |
|
|
значения для параметров |
|
|
|
if result < 0: result = 0 |
# если результат функции |
|
|
меньше нуля, то возвращаем 0 |
|
|
|
return result |
|
|
|
|
|
|
|
return output_func |
|
|
|
|
|
|
#определение оригинальной функции
@check
def sum(a, b): return a + b
#вызов оригинальной функции result1 = sum(10, 20)
print(result1) |
# |
30 |
result2 = sum(10, -20) |
|
|
print(result2) |
# |
0 |
|
|
|
Здесь определена функция sum(), которая возвращает сумму чисел. В декораторе check проверяем результат функции и для простоты, если он меньше нуля, то возвращаем 0.
Консольный вывод программы:
30
0
Задания
Создаваемые функция должны быть снабжены документацией в виде многострочного комментария, включающего в себя: назначение функции, описание каждого параметра, описание возвращаемого значения, дополнительную информацию, например, формулы, если используются.
Формат оформления документации:
def function_name(param1, param2):
"""
Краткое описание назначения функции.
Параметры:
positional1 (тип) - описание обязательного параметра positional2 (тип) - описание обязательного параметра named1 (тип) - описание (по умолчанию: default1) named2 (тип) - описание (по умолчанию: default2)
Возвращает:
тип - описание возвращаемого значения
Примечания:
Дополнительная информация, формулы и т.д.
"""
# тело функции return result
После создания функции:
•Вызовите эту функцию с тестовыми данными.
•Результат выведите на экран, используя f-строку или метод .format().
•Числовые результаты должны быть отформатированы для удобства восприятия: o Используйте округление до разумного количества знаков после запятой. o Для больших/маленьких чисел применяйте инженерную нотацию (например, 1.5e-3) или подбирайте соответствующие единицы
измерения.
o Форматируйте вывод в соответствии со смыслом задачи (добавляйте единицы измерения, пояснения).
Задание 1
Напишите функцию с позиционными параметрами, которая решает конкретную задачу. Функция должна принимать данные через параметры, выполнять вычисления
ивозвращать результат.
1.Расчёт сопротивления цепи. Функция принимает 3 сопротивления (R1, R2, R3)
итип соединения («series» для последовательного, «parallel» для параллельного). Возвращает общее сопротивление цепи.
2.Расчёт мощности. Функция принимает напряжение U и силу тока I. Возвращает мощность P по формуле P = U * I.
3.Закон Ома для участка цепи. Функция принимает три аргумента: первый и второй аргумент – это значения двух известных величин (напряжение, ток или сопротивление), третий аргумент – строка, указывающая, что нужно найти ('U', 'I' или 'R'). Функция возвращает искомую величину.
4.Делитель напряжения. Функция принимает входное напряжение Uin и два
сопротивления R1, R2. Возвращает выходное напряжение Uout по формуле
Uout = Uin * (R2 / (R1 + R2)).
5.Расчёт энергии. Функция принимает мощность P в Ваттах и время t в часах. Возвращает потреблённую энергию W в Ватт-часах.
6.Расчёт ёмкости последовательно соединённых конденсаторов. Функция принимает ёмкости двух конденсаторов (C1, C2). Возвращает общую ёмкость.
7.Расчёт ёмкости параллельно соединённых конденсаторов. Функция принимает ёмкости двух конденсаторов (C1, C2). Возвращает общую ёмкость.
8.Код резистора (3 полосы). Функция принимает три целых числа (цвета первых трёх полос). Возвращает значение сопротивления в Омах. Например, для (1, 0,
1)вернёт 100.
9.Расчёт реактивного сопротивления катушки. Функция принимает индуктивность L в Генри и частоту f в Герцах. Возвращает реактивное сопротивление XL по формуле XL = 2 * π * f * L. Значение π использовать как 3.14159.
10.Расчёт реактивного сопротивления конденсатора. Функция принимает ёмкость C в Фарадах и частоту f в Герцах. Возвращает реактивное сопротивление XC по формуле XC = 1 / (2 * π * f * C). Значение π использовать как 3.14159.
11.Расчёт резонансной частоты LC-цепи. Функция принимает индуктивность L и ёмкость C. Возвращает резонансную частоту.
12.Расчёт постоянной времени RC-цепи. Функция принимает сопротивление R и ёмкость C. Возвращает постоянную времени τ.
13.Расчёт скважности импульсов. Функция принимает длительность импульса timp и период следования импульсов T. Возвращает скважность.
14.Расчёт среднеквадратичного значения RMS напряжения. Функция принимает амплитудное значение напряжения Uamp. Возвращает RMS значение для синусоидального сигнала Urms = Uamp / √2.
15.Пересчёт температуры. Функция принимает температуру в Цельсиях и возвращает её значение в Кельвинах.
16.Расчёт сопротивления светодиодного ограничительного резистора. Функция принимает напряжение питания Usupply, прямое напряжение светодиода Uled и желаемый ток светодиода Iled. Возвращает значение сопротивления резистора по
формуле R = (Usupply - Uled) / Iled.
17.Расчёт коэффициента усиления неинвертирующего усилителя. Функция
принимает два сопротивления (R1, R2). Возвращает коэффициент усиления по напряжению.
18.Расчёт коэффициента усиления инвертирующего усилителя. Функция принимает два сопротивления (Rin, Rf). Возвращает коэффициент усиления по напряжению.
19.Расчёт частоты среза RC-фильтра нижних частот. Функция принимает сопротивление R и ёмкость C. Возвращает частоту среза.
20.Калькулятор цветового кода 4-полосного резистора. Функция принимает 4 целых числа (коды цветов полос). Возвращает значение сопротивления и допуск. (Первые две полосы - цифры, третья - множитель, четвёртая - допуск).
21.Расчёт импеданса последовательной RL-цепи. Функция принимает сопротивление R, индуктивность L и частоту f. Возвращает модуль импеданса Z.
22.Расчёт импеданса последовательной RC-цепи. Функция принимает сопротивление R, ёмкость C и частоту f. Возвращает модуль импеданса Z.
23.Расчёт количества теплоты, выделяющейся на резисторе. Функция принимает силу тока I, сопротивление R и время t. Возвращает количество теплоты по закону Джоуля-Ленца.
24.Расчёт коэффициента заполнения. Функция принимает длительность импульса thigh и период T. Возвращает коэффициент заполнения в процентах.
25.Расчёт эффективного значения тока для прямоугольных импульсов. Функция принимает амплитуду тока Iamp и коэффициент заполнения D. Возвращает эффективное значение тока Irms = Iamp * √D.
26.Расчёт индуктивного сопротивления по известному току и напряжению. Функция принимает действующее значение напряжения U и тока I на катушке. Возвращает индуктивное сопротивление XL.
27.Расчёт ёмкостного сопротивления по известному току и напряжению. Функция принимает действующее значение напряжения U и тока I на конденсаторе. Возвращает ёмкостное сопротивление XC.
28.Расчёт добротности Q последовательного колебательного контура. Функция принимает индуктивность L, ёмкость C и сопротивление потерь R. Возвращает добротность.
29.Калькулятор логических уровней. Функция принимает напряжение на выходе цифровой микросхемы Uout и пороговые напряжения для логического '0' (UL) и логической '1' (UH). Возвращает строку '0' или '1', или 'Undefined', если напряжение находится между порогами.
30.Расчёт времени нарастания сигнала в RC-цепи. Функция принимает сопротивление R, ёмкость C и возвращает время нарастания сигнала от 10 % до 90 % от установившегося значения.
Задание 2
Создайте функцию с позиционными и именованными аргументами, включая аргументы со значениями по умолчанию. Функция должна принимать данные через параметры, выполнять вычисления и возвращать результат.
1.Расчёт коэффициента передачи и выходного напряжения RC-фильтра. Функция принимает сопротивление R (поз.), ёмкость C (поз.), частоту f (имен., по умолчанию 1000 Гц) и входное напряжение (имен., по умолчанию 1.0 В). Формулы: Xc = 1 / (2 × 3.14159 × f × C), K = R / √(R² + Xc²), Vout = Vin × K. Возвращает коэффициент передачи K и выходное напряжение Vout.
2.Расчёт амплитуды и эффективного значения сигнала. Функция принимает максимальное значение Vmax (поз.), минимальное значение Vmin (поз.), тип сигнала (имен., по умолчанию 'sine') и смещение Voffset (имен., по умолчанию 0 В). Формулы: A = (Vmax - Vmin) / 2, Vrms = A × 0.707 + Voffset (для синуса). Возвращает амплитуду A и среднеквадратичное значение Vrms.
3.Расчёт выходного напряжения и мощности делителя с нагрузкой. Функция принимает сопротивление R1 (поз.), сопротивление R2 (поз.), входное напряжение Vin (имен., по умолчанию 12 В) и ток нагрузки Iload (имен., по умолчанию 0 А). Формулы: Vout = Vin × R2 / (R1 + R2), P = Vout × Iload. Возвращает выходное напряжение Vout и мощность нагрузки P.
4.Расчёт выходного напряжения и входного тока неинвертирующего усилителя. Функция принимает сопротивление R1 (поз.), сопротивление R2 (поз.), входное напряжение (имен., по умолчанию 1.0 В) и напряжение питания Vin (имен., по умолчанию 15 В). Формулы: Vout = Vin × (1 + R2/R1), Iin = Vin / (R1 × 1000). Возвращает выходное напряжение Vout и входной ток Iin.
5.Расчёт сопротивления и мощности ограничительного резистора для светодиодов. Функция принимает напряжение питания Vsupply (поз.), напряжение светодиода Vled (поз.), ток светодиода Iled (имен., по умолчанию 0.02 А) и количество светодиодов (имен., по умолчанию 1). Формулы: R = (Vsupply - Vled × N) / Iled, P = Iled² × R. Возвращает сопротивление R и мощность резистора P.
6.Расчёт сопротивления и его изменения от температуры. Функция принимает номинальное сопротивление R0 (поз.), температуру T (поз.), ТКС α (имен., по умолчанию 0.004) и опорную температуру T0 (имен., по умолчанию 20°C). Формулы: R = R0 × [1 + α × (T - T0)], ΔR = R - R0. Возвращает фактическое сопротивление и его изменение.
7.Расчёт различных видов мощности в цепи постоянного тока. Функция принимает напряжение U (поз.), ток I (поз.), КПД источника ηsource (имен., по умолчанию 0.85) и КПД нагрузки ηload (имен., по умолчанию 0.9). Формулы: Pinput = U × I, Poutput = Pinput ×
ηsource, Puseful = Poutput × ηload, Ploss = Pinput - Puseful. Возвращает входную Pinput, выходную Poutput,
полезную Puseful мощность и потери Ploss.
8.Преобразование напряжения в цифровой код и обратно в напряжение АЦП.
Функция принимает входное напряжение Vin (поз.), разрядность АЦП n (имен., по умолчанию 12 бит), опорное напряжение Vref (имен., по умолчанию 3.3 В) и
