diff --git a/TEMA7/pictures/Figure_1.png b/TEMA7/pictures/Figure_1.png new file mode 100644 index 0000000..b9e6fc9 Binary files /dev/null and b/TEMA7/pictures/Figure_1.png differ diff --git a/TEMA7/pictures/{1E0E40B4-99EF-4436-9890-9562FE3626D8}.png b/TEMA7/pictures/{1E0E40B4-99EF-4436-9890-9562FE3626D8}.png new file mode 100644 index 0000000..26dd203 Binary files /dev/null and b/TEMA7/pictures/{1E0E40B4-99EF-4436-9890-9562FE3626D8}.png differ diff --git a/TEMA7/report.md b/TEMA7/report.md index 488d9fa..4658ceb 100644 --- a/TEMA7/report.md +++ b/TEMA7/report.md @@ -2,6 +2,612 @@ Коломейцев Дмитрий, А-02-23 -## Тема 7. Создание пользовательских функций +## Создание пользовательских функций -## 1. \ No newline at end of file +### 1. Установка рабочего каталога. Создание рабочего протокола. + +В оболочке IDLE установил актуальный рабочий каталог, а затем в нём создал рабочий протокол. + +![alt text](pictures/{1E0E40B4-99EF-4436-9890-9562FE3626D8}.png) + +### 2. Создание пользовательской функции. + +Создание функции предполагает выполнение трех операций: формирование функции, ее сохранение и использование. +В общем виде функция в языке Python представляется так:
+__def <Имя функции>([<Список аргументов >]):__
+<отступы> """<Комментарий по назначению функции>"""
+<отступы> <Блок инструкций – тело функции>
+<отступы> return <Значение или вычисляемое выражение>
+ +Именование пользовательских функций производится в соответствии с теми же правилами, что используются при именовании переменных. Также важно заметить, что наличие инструкции __return__ не обязательно для работы функции. + +#### 2.1. Функция без аргументов. + +Функции могут быть без аргументов, как в примере ниже. + +```py +>>> def uspeh(): # Аргументы отсутствуют +... """Подтверждение успеха операции""" +... print("Выполнено успешно!") +... +>>> uspeh() + Выполнено успешно! +``` + +Важно заметить, что многострочный комментарий в начале функции выступает в качестве описания её работы, выводимого при запросе __help__ для этой функции. Поэтому данная справочная информация о функции должна быть хорошо и точно структурирована и расписана, чтобы пользователь мог понять как работать с функцией. + +```py +>>> type(uspeh) # Определение класса пользовательской функции + +>>> dir() # Проверка появления имени функции в пространстве имен + ['__annotations__', '__builtins__', '__doc__', '__loader__', '__name__', '__package__', '__spec__', 'os', 'uspeh'] +>>> help(uspeh) # Получение справки о пользовательской функции + Help on function uspeh in module __main__: + + uspeh() + Подтверждение успеха операции +``` + +#### 2.2. Функция с аргументами. + +Также функции могут быть с аргументами: + +```py +>>> def sravnenie(a, b): +... """Сравнение a и b""" +... if a > b: +... print(a, "больше", b) +... elif a < b: +... print(a, "меньше", b) +... else: +... print(a, "равно", b) +... +>>> n, m = 16, 5; sravnenie(n, m) + 16 больше 5 +``` + +Данную функцию можно применять и для аргументов, являющихся символьными строками. В таком случае будут поэлементно сравниваться символы строк, до первого различия, в соответствии с кодами символов в таблице Unicode/ASCII. + +```py +>>> sravnenie("Text", "Text but bigger") + Text меньше Text but bigger +>>> sravnenie("abc", "ABC") + abc больше ABC +``` + +#### 2.3. Функция, возвращающая значение. + +Функции могут возвращать определенные значения с помощью инструкции __return__. + +```py +>>> def logistfun(b, a): +... """Вычисление логистической функции""" +... import math +... return a / (1 + math.exp(-b)) +... +>>> v, w = 1, 0.7 +>>> z = logistfun(w, v) +>>> z + 0.6681877721681662 +``` + +#### 2.4. Функция, работающая с разными типами аргументов. + +Некоторые функции можно реализовать так, чтобы они могли работать с аргументами разных типов: + +```py +>>> def slozh(a1, a2, a3, a4): +... """ Сложение значений четырех аргументов""" +... return a1 + a2 + a3 + a4 +... +>>> slozh(1, 2, 3, 4) + 10 +>>> slozh("1", "2", "3", "4") + '1234' +>>> b1 = [1, 2]; b2 = [-1, -2]; b3 = [0, 2]; b4 = [-1, -1] +>>> q = slozh(b1, b2, b3, b4) +>>> q + [1, 2, -1, -2, 0, 2, -1, -1] +``` + +Данная функция может работать и с кортежами, но вот при работе со словарями и множествами уже получается ошибка: + +```py +>>> slozh((1, 2), (3, 4), (5, 6), (7, 8)) # Сложение кортежей + (1, 2, 3, 4, 5, 6, 7, 8) +>>> slozh({"A" : 1, "B" : 2}, {"C" : 3, "D" : 4}, {"E" : 5, "F" : 6}, {"G" : 7, "H" : 8}) # Сложение словарей + Traceback (most recent call last): + File "", line 1, in + slozh({"A" : 1, "B" : 2}, {"C" : 3, "D" : 4}, {"E" : 5, "F" : 6}, {"G" : 7, "H" : 8}) + File "", line 3, in slozh + return a1 + a2 + a3 + a4 + TypeError: unsupported operand type(s) for +: 'dict' and 'dict' +>>> slozh({1, 2}, {3, 4}, {5, 6}, {7, 8}) # Сложение множеств + Traceback (most recent call last): + File "", line 1, in + slozh({1, 2}, {3, 4}, {5, 6}, {7, 8}) + File "", line 3, in slozh + return a1 + a2 + a3 + a4 + TypeError: unsupported operand type(s) for +: 'set' and 'set' +``` + +#### 2.5. Функция, реализующая некоторую модель. + +С помощью функций можно легко реализовывать модели некоторых устройств. Так, например, следующая функция реализует модель устройства, преобразующего вид входного сигнала. + +```py +>>> def inerz(x, T, ypred): +... """Модель устройства с памятью: +... x - текущее значение вх. сигнала, +... T - постоянная времени, +... ypred - предыдущее значение выхода устройства""" +... y = (x + T * ypred) / (T + 1) +... return y +... +>>> sps = [0] + [1] * 100 +>>> spsy = [] # Подготовлен список для значений выходного сигнала +>>> TT = 20 # Постоянная времени +>>> yy = 0 # Нулевое начальное условие +>>> for xx in sps: +... yy = inerz(xx, TT, yy) +... spsy.append(yy) +... +>>> import pylab +>>> pylab.plot(spsy) + [] +>>> pylab.xlabel("Время, сек.") + Text(0.5, 0, 'Время, сек.') +>>> pylab.ylabel("Выходной сигнал") +... Text(0, 0.5, 'Выходной сигнал') +>>> pylab.grid(True) +>>> pylab.show() +``` + +Полученный график выходного сигнала: + +![Скриншот полученного графика работы устройства](pictures/Figure_1.png) + +### 3. Функции как объекты. + +#### 3.1. Атрибуты объекта-функции. + +Так как функции являются объектами, то у них есть некоторые атрибуты. Получить их список можно с помощью инструкции dir(). + +```py +>>> dir(inerz) # Получение списка атрибутов объекта-функции + ['__annotations__', '__builtins__', '__call__', '__class__', '__closure__', '__code__', '__defaults__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__get__', '__getattribute__', '__getstate__', '__globals__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__kwdefaults__', '__le__', '__lt__', '__module__', '__name__', '__ne__', '__new__', '__qualname__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__'] +>>> inerz.__doc__ # Использование атрибута объекта-функции + 'Модель устройства с памятью:\n x - текущее значение вх. сигнала,\n T - постоянная времени,\n ypred - предыдущее значение выхода устройства' +>>> help(inerz) # Получение помощи по объекту-функции + Help on function inerz in module __main__: + + inerz(x, T, ypred) + Модель устройства с памятью: + x - текущее значение вх. сигнала, + T - постоянная времени, + ypred - предыдущее значение выхода устройства +``` + +#### 3.2. Ссылка на объект-функцию. + +Ссылку на объект-функцию можно присваивать переменным, а затем обращаться к ним как к самой функции: + +```py +>>> fnkt = sravnenie +>>> v = 16 +>>> fnkt(v, 23) +>>> 16 меньше 23 +``` + +#### 3.3. Альтернативное определение функций. + +Функции могут быть определены разным образом в зависимости от особенностей реализации кода: + +```py +>>> typ_fun = 8 +>>> if typ_fun == 1: +... def func(): +... print("Функция 1") +... else: +... def func(): +... print("Функция 2") +... +>>> func() + Функция 2 +``` + +### 4. Аргументы функции. + +#### 4.1. Использование фунции в качестве аргумента. + +В качестве аргумента функции может выступать и другая функция: + +```py +>>> def fun_arg(fff, a, b, c): +... """fff - имя функции, используемой в качестве аргумента""" +... return(a + fff(c, b)) +... +>>> zz = fun_arg(logistfun, -3, 1, 0.7) +>>> zz + -2.3318122278318336 +``` + +#### 4.2. Обязательные и необязательные аргументы. + +Аргументы функции могут быть необязательными, т.е. иметь некоторое значение, заданное по умолчанию: + +```py +>>> def logistfun(a, b = 1): +... """Вычисление логистической функции""" +... import math +... return b / (1 + math.exp(-a)) +... +>>> logistfun(0.7) + 0.6681877721681662 +>>> logistfun(0.7, 2) + 1.3363755443363323 +``` + +#### 4.3. Расположение аргументов функции. + +К функции можно обращаться с произвольным (непозиционным) расположением аргументов, при этом необходимо указывать их имена: + +```py +>>> logistfun(b = 0.5, a = 0.8) + 0.34498724056380625 +>>> logistfun(0.8, 0.5) + 0.34498724056380625 +``` + +#### 4.4. Аргументы функции, содержащиеся в списке или кортеже. + +Аргументы функции могут содержаться в списке или кортеже, в таком случае при их передаче в функцию необходима распаковка с помощью оператора " __*__ ". + +```py +>>> b1234 = [b1, b2, b3, b4] +>>> slozh(*b1234) + [1, 2, -1, -2, 0, 2, -1, -1] +>>> slozh(b1, b2, b3, b4) + [1, 2, -1, -2, 0, 2, -1, -1] +``` + +#### 4.5. Аргументы функции, содержащиеся в словаре. + +Аналогичная ситуация происходит и с аргументами, представленными в виде словаря. Однако распаковка в таком случае проводится с помощью оператора " __**__ ". Важно также заметить, что имена ключей словаря с аргументами не должны совпадать с именами остальных переданных аргументов, иначе произойдет ошибка. + +```py +>>> dic4 = {"a1" : 1, "a2" : 2, "a3" : 3, "a4" : 4} +>>> slozh(**dic4) + 10 +``` + +#### 4.6. Смешанные ссылки. + +Данные способы передачи аргументов в функцию можно комбинировать: + +```py +>>> e1 = (-1, 6) +>>> dd2 = {"a3" : 3, "a4" : 4} +>>> slozh(*e1, **dd2) + 12 +``` + +#### 4.7. Переменное число аргументов у функции. + +Число аргументов у функции может быть произвольным, что осуществляется с помощью того же оператора " __*__ ". + +```py +>>> def func4(*kort7): +... """Произвольное число элементов в составе кортежа""" +... smm = 0 +... for el in kort7: +... smm += el +... return smm +... +>>> func4(-1, 2) + 1 +>>> func4(-1, 2, 0, 3, 6) + 10 +``` + +#### 4.8. Комбинация аргументов. + +Данные способы передачи аргументов также можно комбинировать: + +```py +>>> def func4(a, b = 7, *kort7): +... """Кортеж - сборка аргументов - должен быть последним!""" +... smm = 0 +... for el in kort7: +... smm += el +... return a * smm + b +... +>>> func4(-1, 2, 0, 3, 6) + -7 +``` +Пример реализации аналогичной функции для произвольного количества аргументов, переданного в виде словаря: + +```py +>>> def func4(a, b = 7, **dict7): +... """Словарь - сборка аргументов - должен быть последним!""" +... smm = 0 +... for el in dict7.values(): +... smm += el +... return a * smm + b +... +>>> func4(-1, 2, **{"a1" : 0, "a2" : 3, "a3" : 6}) + -7 +``` + +#### 4.9. Изменение значений объектов с помощью функций. + +С помощью функций можно изменять значения переменных - объектов изменяемого типа: + +```py +>>> a = 90 +>>> def func3(b): +... b = 5 * b + 67 +... +>>> func3(a) +>>> a # Числовой объект является неизменяемым + 90 +>>> sps1 = [1, 2, 3, 4] +>>> def func2(sps): +... sps[1] = 99 +... +>>> func2(sps1) +>>> sps1 # Список - изменяемый объект + [1, 99, 3, 4] +>>> kort = (1, 2, 3, 4) +>>> func2(kort) # Кортеж также является неизменяемым + Traceback (most recent call last): + File "", line 1, in + func2(kort) + File "", line 2, in func2 + sps[1] = 99 + TypeError: 'tuple' object does not support item assignment +``` + +### 5. Специальные типы пользовательских функций. + +#### 5.1. Анонимные функции. + +Анонимные функции - лямбда-функциями - это функции без имени , определяемые по следующей схеме:
+__lambda [<Список аргументов >]: <Возвращаемое значение или выражение>__
+Анонимная функция возвращает ссылку на объект-функцию, которую можно присвоить другому объекту. + +```py +>>> anfun1 = lambda: 1.5 + math.log10(12.23) +>>> anfun1() + 2.5874264570362855 +>>> anfun2 = lambda a, b: a + math.log10(b) +>>> anfun2(17, 234) + 19.369215857410143 +>>> anfun3 = lambda a, b = 234: a + math.log10(b) +>>> anfun3(100) + 102.36921585741014 +``` + +#### 5.2. Функции-генераторы. + +Функции-генераторы - функции, использующиеся в итерационных процессах, позволяющие на каждой из итераций получать значение с помощью инструкции __yield__, приостанавливающей выполнение функции. + +```py +>>> def func5(diap, shag): +... """Итератор, возвращающий значения из диапазона от 1 до diap с шагом shag""" +... for i in range(1, diap + 1, shag): +... yield i +... +>>> for mm in func5(7, 3): +... print(mm) +... + 1 + 4 + 7 +``` + +При работе с такими функциями часто используют метод "__ next __", активирующий очередную итерацию выполнения функции: + +```py +>>> alp = func5(7, 3) +>>> print(alp.__next__()) + 1 +>>> print(alp.__next__()) + 4 +>>> print(alp.__next__()) + 7 +>>> print(alp.__next__()) # При отсутствии следующих итераций будет ошибка + Traceback (most recent call last): + File "", line 1, in + print(alp.__next__()) + StopIteration +``` + +### 6. Локализация объектов. + +По отношению к функции все объекты подразделяются на локальные и глобальные. Локальными являются объекты, которые создаются в функциях присваиванием им некоторых значений. Они записываются в пространство имен, создаваемое в функции. Глобальные – это те объекты, значения которых заданы вне функции. Они определены в пространствах имен вне функции. + +#### 6.1. Примеры на локализацию объектов в функциях. + +Локальный и глобальный объекты могут иметь одинаоковое имя: + +```py +>>> glb = 10 +>>> def func7(arg): +... loc1 = 15 +... glb = 8 +... return loc1 * arg +... +>>> func7(glb) + 150 +>>> glb # Значение не измени лось, т.к. операции проводились над локальной переменной + 10 +``` + +При использовании локального объекта до его определения будет ошибка: + +```py +>>> def func8(arg): +... loc1 = 15 +... print(glb) +... glb = 8 +... return loc1 * arg +... +>>> func8(glb) + Traceback (most recent call last): + File "", line 1, in + func8(glb) + File "", line 3, in func8 + print(glb) + UnboundLocalError: cannot access local variable 'glb' where it is not associated with a value +``` + +Локализацию объекта можно переопределить с помощью дескриптора __global__: + +```py +>>> glb = 11 +>>> def func7(arg): +... loc1 = 15 +... global glb +... print(glb) +... glb = 8 +... return loc1 * arg +... +>>> func7(glb) + 11 + 165 +>>> glb # Значение изменилось, т.к. была переопределена локализация объекта + 8 +``` + +#### 6.2. Функции для выявления локализации объектов. + +Чтобы узнать текущую локализацию объекта можно использовать функции __globals()__ и __locals()__, которые возвращают словари с ключами - именами объектов, являющихся, соответственно, глобальными или локальными на уровне вызова этих функций. + +```py +>>> globals().keys() + dict_keys(['__name__', '__doc__', '__package__', '__loader__', '__spec__', '__annotations__', '__builtins__', 'func4', 'a', 'func3', 'sps1', 'func2', 'kort', 'anfun1', 'math', 'anfun2', 'anfun3', 'func5', 'mm', 'alp', 'glb', 'func7', 'func8']) +>>> locals().keys() + dict_keys(['__name__', '__doc__', '__package__', '__loader__', '__spec__', '__annotations__', '__builtins__', 'func4', 'a', 'func3', 'sps1', 'func2', 'kort', 'anfun1', 'math', 'anfun2', 'anfun3', 'func5', 'mm', 'alp', 'glb', 'func7', 'func8']) +``` + +Пример просмотра локальных и глобальных объектов изнутри функциии: + +```py +>>> def func8(arg): +... loc1 = 15 +... glb = 8 +... print(globals().keys()) +... print(locals().keys()) +... return loc1 * arg +... +>>> func8(glb) + dict_keys(['__name__', '__doc__', '__package__', '__loader__', '__spec__', '__annotations__', '__builtins__', 'func4', 'a', 'func3', 'sps1', 'func2', 'kort', 'anfun1', 'math', 'anfun2', 'anfun3', 'func5', 'mm', 'alp', 'glb', 'func7', 'func8']) + dict_keys(['arg', 'loc1', 'glb']) + 150 +>>> "glb" in globals().keys() + True +``` + +#### 6.3. Локализация объектов во вложенных функциях. + +Локальные переменные будут различаться на разных уровнях вложенных функций: + +```py +>>> def func9(arg2, arg3): +... def func9_1(arg1): +... loc1 = 15 +... glb1 = 8 +... print("glob_func9_1:", globals().keys()) +... print("locl_func9_1:", locals().keys()) +... return loc1 * arg1 +... loc1 = 5 +... glb = func9_1(loc1) +... print("glob_func9:", globals().keys()) +... print("locl_func9:", locals().keys()) +... return arg2 + arg3 * glb +... +>>> func9(10, 1) + glob_func9_1: dict_keys(['__name__', '__doc__', '__package__', '__loader__', '__spec__', '__annotations__', '__builtins__', 'func4', 'a', 'func3', 'sps1', 'func2', 'kort', 'anfun1', 'math', 'anfun2', 'anfun3', 'func5', 'mm', 'alp', 'glb', 'func7', 'func8', 'func9']) + locl_func9_1: dict_keys(['arg1', 'loc1', 'glb1']) + glob_func9: dict_keys(['__name__', '__doc__', '__package__', '__loader__', '__spec__', '__annotations__', '__builtins__', 'func4', 'a', 'func3', 'sps1', 'func2', 'kort', 'anfun1', 'math', 'anfun2', 'anfun3', 'func5', 'mm', 'alp', 'glb', 'func7', 'func8', 'func9']) + locl_func9: dict_keys(['arg2', 'arg3', 'func9_1', 'loc1', 'glb']) + 85 +``` + +#### 6.4. Моделирование некоторой системы с помощью нескольких функций. + +Моделирование системы, состоящей из последовательного соединения реального двигателя, охваченного отрицательной обратной связью с тахогенератором в ней, и нелинейного звена типа "зона нечувствительности", при подаче на нее синусоидального входного сигнала. + +Этап 1 - запрос и обработка введенных параметров системы: + +```py +>>> znach = input("k1, T, k2, Xm, A, F, N = ").split(",") +>>> k1, T, k2, Xm, A, F, N = 7, 4, 2, 5, 2, 0.01, 100 +>>> k1 = float(znach[0]) +>>> T = float(znach[1]) +>>> k2 = float(znach[2]) +>>> Xm = float(znach[3]) +>>> A = float(znach[4]) +>>> F = float(znach[5]) +>>> N = int(znach[6]) +``` + +Этап 2 - реализация входного сигнала: + +```py +>>> import math +>>> vhod = [] +>>> for i in range(N): +... vhod.append(A * math.sin((2 * i * math.pi) / F)) +... +>>> vhod + [0.0, 7.857546894913888e-15, 1.5715093789827776e-14, -2.038010347584904e-13, 3.143018757965555e-14, -6.428332918551267e-13, -4.076020695169808e-13, -1.081865548951763e-12, ..., -7.666359036382766e-12, -6.521633112271693e-12, -5.376907188160619e-12, -1.8784096492416397e-11, -3.0874553399384703e-12] +``` + +Этап 3 - создание функций, реализующих компоненты системы: + +```py +>>> def realdvig(xtt, kk1, TT, yti1, ytin1): +... # Модель реального двигателя +... yp = kk1 * xtt # Усилитель +... yti1 = yp + yti1 # Интегратор +... ytin1 = (yti1 + TT * ytin1) / (TT + 1) +... return [yti1, ytin1] +... +>>> def tahogen(xtt, kk2, yti2): +... # Модель тахогенератора +... yp = kk2 * xtt +... yti2 = yp + yti2 +... return yti2 +... +>>> def nechus(xtt, gran): +... # Зона нечувствительности +... if xtt < gran and xtt > (-gran): +... return 0 +... elif xtt >= gran: +... return xtt - gran +... elif xtt <= (-gran): +... return xtt + gran +``` + +Этап 4 - соединение компонент в соответствии с заданием и получение выходного сигнала: + +```py +>>> yi1 = 0; yin1 = 0; yi2 = 0 +>>> vyhod = [] +>>> for xt in vhod: +... xt1 = xt - yi2 +... [yi1, yin1] = realdvig(xt1, k1, T, yi1, yin1) +... yi2 = tahogen(yin1, k2, yi2) +... yt = nechus(yin1, Xm) +... vyhod.append(yt) +... +>>> print("y =", vyhod) + y = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2.0750309723388316, 0, -12.800524758874488, 11.328734010636943, 37.9986846091337, -51.695128234754044, -93.73359277523646, 176.80628109766909, 206.3512386278131, -546.6832050741272, -399.06819555417735, 1598.4573240949626, 604.2307443815814, -4487.243599090263, -296.234076116122, 12162.217953139934, -2805.586281370296, -31870.75393905672, 17036.29869407474, 80623.4912164512, -69802.97975583967, -195996.03820751337, 245998.54033834403, 453751.31553486304, -796405.0354457049, -982958.5881199688, 2433666.144586724, 1918572.300755354, -7113910.846421458, -3041359.0662945407, 20031038.041300073, 2216408.8952286365, -54513798.16041583, 10262153.3054456, 143509014.33326405] +``` + + +### 7. Завершение работы со средой. + +Сохранил файлы отчета в своем рабочем каталоге и закончил сеанс работы с IDLE.