2010-10-12_Functional_2

advertisement
Функции и функциональное
программирование
 Функция – фрагмент кода, который можно вызывать
из любого места программы.
Например, с помощью функции len() можно получать
количество элементов последовательности.
Функции позволяют уменьшить избыточность
программного кода и повысить его
структурированность.
Создание функции и ее вызов
Функция описывается с помощью ключевого слова def:
def <Имя функции>([Параметры]):
[" " " Строка документирования " " "]
<Тело функции>
[return <Значение>]
 Имя функции должно быть уникальным
идентификатором, состоящим из латинских букв, цифр и
знаков подчеркивания, причем имя функции не может
начинаться с цифры
 В качестве имени нельзя использовать ключевые слова;
кроме того, следует избегать совпадений с названиями
встроенных идентификаторов
 Регистр символов в названии имеет значение!
 Если тело функции не содержит выражений, то внутри
необходимо разместить оператор pass. Этот оператор удобно
использовать на этапе отладки программы, когда мы определили
функцию, а тело будем дописывать позже:
def f_pass():
#данная программа
pass
ничего не делает
 return(необязательная) позволяет вернуть значение из функции.
После использования этой инструкции выполнение функции
будет остановлено:
def f_test():
print "Текст до инструкции return "
return "Возвращаемое значение "
print «Это выражение никогда не будет выполнено "
print f_test()
# Вызываем функцию
Инструкции return может не быть вообще, тогда выполняются
выражения внутри функции и возвращается значение None
Определение функций:
def f_print_ok():
""" Пример функции без параметров """
print """Сообщение при удачно выполненной операции"
def f_print(m):
""" Пример функции с параметром """
print m
def f_sum(x, y):
""" Пример функции с параметрами, возвращающей сумму двух переменных """
return x + y
Вызов функций:
f_print_ok()
f_print("Сообщение")
v1 = f_sum(5, 2)
a, b = 10, 50
v2 = f_sum(a, b)
# Вызываем функцию без параметров
# Функция выведет сообщение
# Переменной v1 будет присвоено значение 7
# Переменной v2 будет присвоено значение 60
Как видно из примера, имя переменной в вызове функции может не совпадать с
именем переменной в определении функции. Количество же параметров в
определении функции должно совпадать с количеством параметров при вызове!
Оператор +, используемый в f_sum() , применяется не только для сложения, но и
позволяет объединить последовательности.
Расположение определений функций
 все выражения в
программе выполняются
последовательно сверху
вниз, это означает, что
прежде чем
использовать
идентификатор, его
необходимо
предварительно
объявить, присвоив ему
значение. Поэтому
определение функции
должно быть
расположено перед
вызовом функции.
Правильно:
def f_sum(x, y):
return x + y
v = f_sum(10, 20)
# Вызываем после
определения, все нормально
Неправильно:
v = f_sum(10, 20) # Идентификатор еще не
def f_sum(x, y):
определен, это ошибка
return x + y
С помощью оператора ветвления if можно
изменить порядок выполнения программы. Таким
образом, можно разместить внутри условия
несколько определений функций с одинаковым
названием, но разной реализацией.
Необязательные параметры
 Чтобы сделать некоторые параметры
необязательными, нужно в определении функции
присвоить этому параметру начальное значение.
def f_sum(x, y=2): # y - необязательный параметр
return x + y
v1 = f_sum(5)
# Переменной v1 будет присвоено значение 7
v2 = f_sum(10, 50) # Переменной v2 будет присвоено значение 60
 таким образом, если второй параметр не задан, то его значение
будет 2
 необязательные параметры, должны следовать после
обязательных, иначе выведется отчет об ошибке
Сопоставление по ключам

Можно передавать значение в функцию, используя сопоставление
по ключам – для этого при вызове функции параметрам
присваиваются значения
 Последовательность указания параметров произвольная
def f_sum(x, y):
return x + y
print f_sum(y=20, x=10)
#Сопоставление по ключам

Сопоставление по ключам очень удобно использовать, если в функции
несколько необязательных параметров, тогда не нужно перечислять все
значения, а достаточно присвоить значение нужному параметру:
def f_sum(a=2, b=3, c=4): # Все параметры необязательны
return a + b + c
print f_sum(2, 3, 20)
# Позиционное присваивание
print f_sum(c=20)
# Сопоставление по ключам
Передача значений из кортежа
и списка
 если значения параметров, которые планируется
передать в функцию, содержатся в кортеже, то
перед объектом следует указать символ *:
def f_sum(a, b, c):
return a + b + c
t1, arr = (1, 2, 3), [1, 2, 3]
print f_sum(*t1)
# Распаковываем кортеж
print f_sum(*arr)
# Распаковываем список
t2 = (2, 3)
print f_sum(1, *t2)
# Можно комбинировать значения
Передачи значений из словаря
 если значения параметров содержатся в словаре, то
распаковывать словарь можно, указав перед ним две
звездочки **:
def f_sum(a, b, c):
return a + b + c
d1, {"a": 1, "b": 2, "c": 3}
print f_sum(**d1)
# Распаковываем словарь
t, d2 = (1, 2), {"с": 3}
print f_sum(*t, **d2) # Можно комбинировать значения
 Распаковать кортежи, списки и словари позволяет также функция
apply()(устаревшая функция)
apply(<Ссылка на функцию>[, <Кортеж или список>[, <Словарь>]])
Переменное число параметров в
функции
 если перед параметром в определении функции указать *,то функции
можно будет передать произвольное количество параметров. Все
переданные параметры сохраняются в кортеже;
Суммирование произвольного количества чисел:
def f_sum(*t):
"""Функция принимает произвольное кол-во параметров"""
res = 0
for i in t
# Перебираем кортеж с переданными параметрами
res += i
return res
print f_sum(10, 20)
# Выведет: 30
print f_sum(10, 20, 30, 40, 50, 60) # Выведет 210
Анонимные функции – у них нет имени!
то есть лямбда-функции, описывается с помощью ключевого слова
lambda:
lambda [<Параметр1>[, …, <ПараметрN>]]: <Возвращаемое значение >
 в качестве значения лямбда-
f1 = lambda: 10 + 20 # Функция
без параметров
f2 = lambda x, y: x + y # Функция с 2
параметрами
f3 = lambda x, y, z: x + y + z # с 3
print f1()
# Выведет: 30
print f2(5, 10)
# Выведет : 15
print f3(5, 10, 30) # Выведет: 45
функция возвращает ссылку
на объект-функцию, которую
можно сохранить в
переменной или передать в
качестве параметра в другую
функцию
 вызывать как обычную, с
помощью круглых скобок,
внутри которых передаваемые
параметры
 также могут быть
необязательные параметры
Функции-генераторы
 функция-генератор – функция, которая может возвращать одно значение из
нескольких значений на каждой итерации
 приостановить выполнение и превратить функцию в генератор позволяет
ключевое слово yield
Функция, которая возводит элементы последовательности в указанную
степень:
def f_test(x, y):
for i in xrange(1, x + 1):
yield i ** y
for n in f_test(10, 2):
print n,
print
for n in f_test(10, 3):
print n,
# Выведет: 1 4 9 16 25 36 49 64 81 100
# Вставляем пустую строку
# Выведет: 1 8 27 64 125 216 343 512 729 1000
Декораторы функций
 декораторы позволяют изменить поведение обычных функций,
например, выполнить какие то действия перед выполнением
функции:
def f_deco(f):
# Функция-декоратор
print "f_test(f)"
return f
# Возвращаем ссылку на функцию
@f_deco
Выведет:
def f_test(x) :
Вызвана функция f_test()
return "x = %s" % x
х = 10
print f_test(10)
 перед определением функции f_test указывается название функции
f_deco с предваряющим символом @: @f_deco. Таким образом f_deco
становится декоратором функции f_test; в качестве параметра функция-
декоратор принимает ссылку на функцию, поведение которой нужно
изменить, и должна возвращать ссылку на ту же функцию или какую –
любо другую
Рекурсия, вычисление факториала
Рекурсия – возможность функции вызывать саму себя. Её удобно
использовать для перебора объекта, имеющего заранее неизвестную
структуру, или выполнения неопределенного количества операций
# -*- coding: ср1251 -*def factorial(n):
if == 0 or n == 1: return 1
else:
return n * factorial(n - 1)
while True:
x = raw_input("Введите число: ")
if x.isdigit(): # Если строка содержит только цифры
x = int(x)
# Преобразуем строку в число
break
# Выходим из цикла
else:
print "Вы ввели не число"
print "Факториал числа %s = %s" % (x, factorial(x))
Глобальные и локальные переменные
 Глобальные переменные – переменные, объявленные в программе вне функции
def f_test(glob2):
print " Значение глобальной переменной glob1 = %s" % glob1
glob2 += 10
print " Значение локальной переменной glob2 = %s" % glob 2
glob1, glob2 = 10, 5
f_test(77)
# Вызываем функцию
print " Значение глобальной переменной glob2 = %s" % glob2
Переменной glob2 внутри функции присваивается
значение параметра, поэтому создается новое имя
glob2, которое является локальным, все изменения
внутри функции не затронут значение одноименной
Результат выполнения:
Значение глобальной переменной glob1 = 10
Значение глобальной переменной glob2 = 87
Значение глобальной переменной glob2 = 5
глобальной переменной.
 Локальные переменные – те, которым внутри функции присваивается значение.
Если имя локальной совпадает с глобальной, то все операции внутри функции
осуществляются с локальной переменной, а значение глобальной не
изменяется. Локальные переменные видны только внутри тела функции.
Функциональное программирование – раздел дискретной
математики, в которой процесс вычисления трактуется как вычисление
значений функций в математическом понимании
Функциональное
Императивное
вычисление результатов функций
от исходных данных и результатов
других функций
предполагает явное хранение
состояния программы
выходные данные зависят только
от входных
Опираются на аргументы,
состояние внешних переменных,
могут иметь побочные эффекты
Функции высших порядков – которые в качестве аргументов
могут принимать функции, и возвращать другие функции.
Позволяют использовать карринг - преобразование функции от
пары аргументов в функцию, берущую свои аргументы по одном
Чистые функции не имеют побочных эффектов вводавывода и памяти:
удаление без вреда результата, если он не используется
принцип прозрачности ссылок
удовлетворяет принципам thread-safe
свобода компилятору комбинировать и
реорганизовывать вычисление
Рекурсивные функции - вызывают сами себя, позволяя
операции выполняться снова и снова
Главная особенность:
реализация модели вычислений без состояния(+,-)
Стили программирования
Императивные программы имеют склонность акцентировать
последовательности шагов для выполнения какого-то действия, а
функциональные программы к расположению и композиции функций, часто
не обозначая точной последовательности шагов:
# imperative style
Императивная версия
target = [] # create empty list
for item in source_list: # iterate over each thing in source
trans1 = G(item)
# transform the item with the G() function
trans2 = F(trans1)
# second transform with the F() function
target.append(trans2) # add transformed item to target
# functional style
# FP-oriented languages often have standard compose()
compose2 = lambda A, B: lambda x: A(B(x))
target = map(compose2(F, G), source_list)
Функциональная версия
предпосылки для полноценного функционального
программирования в Python:
функции высших порядков
обработка списков
рекурсия
определяется с помощью оператора def или лямбдавыражением:
def func(x, y):
return x**2 + y**2
эквивалентны
func = lambda x, y: x**2 + y**2
формальные и фактические аргументы, при вызове
фактические:
func(2, y=7)
позиционные аргументы
именованные
повторы в именах аргументов недопустимы!
Списочные выражения - наиболее выразительное из
функциональных средств Питона.
вычисление списка квадратов натуральных чисел,<10:
l = [x**2 for x in range(10)]
Встроенные функции высших порядков
map()
позволяет обрабатывать одну или несколько последовательностей с
помощью заданной функции:
>>> list1 = [7, 2, 3, 10, 12]
>>> list2 = [-1, 1, -5, 4, 6]
>>> map(lambda x, y: x*y, list1, list2)
[-7, 2, -15, 40, 72]
аналогичного (только при одинаковой длине списков) результата
можно добиться с помощью списочных выражений:
>>> [x*y for x, y in zip(list1, list2)]
[-7, 2, -15, 40, 72]
filter()
фильтрует значения последовательности, в результирующем списке
только те значения, для которых значение функции для элемента
истинно:
>>> spisok = [10, 4, 2, -1, 6]
>>> filter(lambda x: x < 5, spisok)
# В результат попадают только те
эл-ты x, для которых x < 5 истинно
[4, 2, -1]
то же самое с помощью списковых выражений:
>>> spisok = [10, 4, 2, -1, 6]
>>> [x for x in spisok if x < 5]
[4, 2, -1]
reduce() – организация цепочечных вычислений в
списке
произведение элементов :
>>> spisok = [2, 3, 4, 5, 6]
>>> reduce(lambda res, x: res*x, spisok, 1)
720
Вычисления происходят в следующем порядке:
((((1*2)*3)*4)*5)*6
Цепочка вызовов связывается с помощью промежуточного
результата (res)
Если список пустой, просто используется третий параметр (в случае
произведения нуля множителей это 1):
>>> reduce(lambda res, x: res*x, [], 1)
1
промежуточный результат необязательно число, это может
быть любой другой тип данных, в том числе и список.
apply()
применения другой функции к позиционным и
именованным аргументам, заданным списком и
словарем:
>>> def f(x, y, z, a=None, b=None):
...
print x, y, z, a, b
...
>>> apply( f, [1, 2, 3], {'a': 4, 'b': 5})
12345
Замыкания – функции, определяемые внутри
других функций
def multiplier(n):
"multiplier(n) возвращает функцию, умножающую на n"
def mul(k):
return n*k
return mul
# того же эффекта можно добиться выражением
# multiplier = lambda n: lambda k: n*k
mul2 = multiplier(2) # mul2 - функция, умножающая на 2,
например, mul2(5) == 10
Итераторы
Применение перечисляющего сортирующего итераторов:
>>> it = enumerate(sorted("PYTHON")) # итератор для
перечисленных отсортированных букв слова
>>> it.next() # следующее значение (0, 'H')
>>> print list(it) # оставшиеся значения в виде списка
[(1, 'N'), (2, 'O'), (3, 'P'), (4, 'T'), (5, 'Y')]
Использование модуля itertools:
>>> from itertools import chain
>>> print list(chain(iter("ABC"), iter("DEF")))
['A', 'B', 'C', 'D', 'E', 'F']
Ленивые выражения
 простейшие логические операции or и and не вычисляют второй операнд,
если результат определяется первым операндом
 лямбда-выражения
 определенные пользователем классы с ленивой логикой вычислений
>>> def f():
...
print "f"
...
return "f"
...
>>> def g():
...
print "g"
...
return “g"
...
>>> f() if True else g()
f
'f''
>>> f() if False else g()
g
'g'
Конец=)
Download