# Отчет по теме 7 Коломейцев Дмитрий, А-02-23 ## Создание пользовательских функций ### 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.