<отступы> """<Комментарий по назначению функции>"""<br>
<отступы> <Блок инструкций – тело функции><br>
<отступы> return <Значение или вычисляемое выражение><br>
Именование пользовательских функций производится в соответствии с теми же правилами, что используются при именовании переменных. Также важно заметить, что наличие инструкции __return__ не обязательно для работы функции.
#### 2.1. Функция без аргументов.
Функции могут быть без аргументов, как в примере ниже.
```py
>>> def uspeh(): # Аргументы отсутствуют
... """Подтверждение успеха операции"""
... print("Выполнено успешно!")
...
>>> uspeh()
Выполнено успешно!
```
Важно заметить, что многострочный комментарий в начале функции выступает в качестве описания её работы, выводимого при запросе __help__ для этой функции. Поэтому данная справочная информация о функции должна быть хорошо и точно структурирована и расписана, чтобы пользователь мог понять как работать с функцией.
```py
>>> type(uspeh) # Определение класса пользовательской функции
<class'function'>
>>> dir() # Проверка появления имени функции в пространстве имен
>>> 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. Функция, работающая с разными типами аргументов.
Некоторые функции можно реализовать так, чтобы они могли работать с аргументами разных типов:
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 = [] # Подготовлен список для значений выходного сигнала
>>> 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. Аргументы функции, содержащиеся в словаре.
Аналогичная ситуация происходит и с аргументами, представленными в виде словаря. Однако распаковка в таком случае проводится с помощью оператора " __**__ ". Важно также заметить, что имена ключей словаря с аргументами не должны совпадать с именами остальных переданных аргументов, иначе произойдет ошибка.
#### 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 "<pyshell#55>", line 1, in <module>
func2(kort)
File "<pyshell#51>", line 2, in func2
sps[1] = 99
TypeError: 'tuple' object does not support item assignment
```
### 5. Специальные типы пользовательских функций.
#### 5.1. Анонимные функции.
Анонимные функции - лямбда-функциями - это функции без имени , определяемые по следующей схеме:<br>
__lambda [<Список аргументов >]: <Возвращаемое значение или выражение>__<br>
Анонимная функция возвращает ссылку на объект-функцию, которую можно присвоить другому объекту.
```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 "<pyshell#78>", line 1, in <module>
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 "<pyshell#97>", line 1, in <module>
func8(glb)
File "<pyshell#96>", 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()__, которые возвращают словари с ключами - именами объектов, являющихся, соответственно, глобальными или локальными на уровне вызова этих функций.
#### 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))