Главная страница

ээдд. Прохоренок_Н_А__Дронов_В_А_Python_3_и_PyQt_5_Разработка_приложен. Николай Прохоренок Владимир Дронов


Скачать 7.92 Mb.
НазваниеНиколай Прохоренок Владимир Дронов
Дата05.05.2023
Размер7.92 Mb.
Формат файлаpdf
Имя файлаПрохоренок_Н_А__Дронов_В_А_Python_3_и_PyQt_5_Разработка_приложен.pdf
ТипДокументы
#1111379
страница20 из 83
1   ...   16   17   18   19   20   21   22   23   ...   83
. Например, в параметре setup можно подключить модуль.
Получить время выполнения можно с помощью метода timeit([number=1000000])
. В пара- метре number указывается количество повторений.
Для примера просуммируем числа от
1
до
10000
тремя способами и выведем время выпол- нения каждого способа (листинг 10.3).
Листинг 10.3. Измерение времени выполнения
# -*- coding: utf-8 -*- from timeit import Timer code1 = """\ i, j = 1, 0 while i < 10001: j += i i += 1
""" t1 = Timer(stmt=code1) print("while:", t1.timeit(number=10000)) code2 = """\ j = 0 for i in range(1, 10001): j += i

Глава 10. Работа с датой и временем
209
""" t2 = Timer(stmt=code2) print("for:", t2.timeit(number=10000)) code3 = """\ j = sum(range(1, 10001))
""" t3 = Timer(stmt=code3) print("sum:", t3.timeit(number=10000)) input()
Примерный результат выполнения (зависит от мощности компьютера): while: 10.487761735853875 for: 6.378136742560729 sum: 2.2042291718107094
Сразу видно, что цикл for работает почти в два раза быстрее цикла while
, а функция sum()
в данном случае вне конкуренции.
Метод repeat([repeat=3][, number=1000000])
вызывает метод timeit()
указанное в пара- метре repeat количество раз и возвращает список значений. Аргумент number передается в качестве параметра методу timeit()
Для примера создадим список со строковыми представлениями чисел от
1
до
10000
: в пер- вом случае для создания списка используем цикл for и метод append()
, а во втором — гене- ратор списков (листинг 10.4).
Листинг 10.4. Использование метода repeat()
# -*- coding: utf-8 -*- from timeit import Timer code1 = """\ arr1 = [] for i in range(1, 10001): arr1.append(str(i))
""" t1 = Timer(stmt=code1) print("append:", t1.repeat(repeat=3, number=2000)) code2 = """\ arr2 = [str(i) for i in range(1, 10001)]
""" t2 = Timer(stmt=code2) print("генератор:", t2.repeat(repeat=3, number=2000)) input()
Примерный результат выполнения: append: [6.27173358307843, 6.222750011887982, 6.239843531272257] генератор: [4.6601598507632325, 4.648098189899006, 4.618446638727157]
Как видно из результата, генераторы списков выполняются быстрее.

ГЛ А В А
11
Пользовательские функции
Функция — это фрагмент кода, который можно вызвать из любого места программы. В пре- дыдущих главах мы уже не один раз использовали встроенные функции языка Python — например, с помощью функции len()
получали количество элементов последовательности.
В этой главе мы рассмотрим создание пользовательских функций, которые позволят уменьшить избыточность программного кода и повысить его структурированность.
11.1. Определение функции и ее вызов
Функция создается (или, как говорят программисты, определяется) с помощью ключевого слова def в следующем формате: def <Имя функции>([<Параметры>]):
[""" Строка документирования """]
<Тело функции>
[return <Результат>]
Имя функции должно быть уникальным идентификатором, состоящим из латинских букв, цифр и знаков подчеркивания, причем имя функции не может начинаться с цифры. В ка- честве имени нельзя использовать ключевые слова, кроме того, следует избегать совпаде- ний с названиями встроенных идентификаторов. Регистр символов в названии функции также имеет значение.
После имени функции в круглых скобках можно указать один или несколько параметров через запятую, а если функция не принимает параметры, указываются только круглые скоб- ки. После круглых скобок ставится двоеточие.
Тело функции представляет собой составную конструкцию. Как и в любой составной кон- струкции, инструкции внутри функции выделяются одинаковым количеством пробелов слева. Концом функции считается инструкция, перед которой находится меньшее количест- во пробелов. Если тело функции не содержит инструкций, то внутри ее необходимо размес- тить оператор pass
, который не выполняет никаких действий. Этот оператор удобно ис- пользовать на этапе отладки программы, когда мы определили функцию, а тело решили дописать позже. Вот пример функции, которая ничего не делает: def func(): pass
Необязательная инструкция return позволяет вернуть из функции какое-либо значение в качестве результата. После исполнения этой инструкции выполнение функции будет остановлено, и последующие инструкции никогда не будут выполнены:

Глава 11. Пользовательские функции
211
>>> def func(): print("Текст до инструкции return") return "Возвращаемое значение" print("Эта инструкция никогда не будет выполнена")
>>> print(func()) # Вызываем функцию
Результат выполнения:
Текст до инструкции return
Возвращаемое значение
Инструкции return может не быть вообще. В этом случае выполняются все инструкции внутри функции, и в качестве результата возвращается значение
None
Для примера создадим три функции (листинг 11.1).
Листинг 11.1. Определение функций def print_ok():
""" Пример функции без параметров """ print("Сообщение при удачно выполненной операции") def echo(m):
""" Пример функции с параметром """ print(m) def summa(x, y):
""" Пример функции с параметрами, возвращающей сумму двух переменных """ return x + y
При вызове функции значения ее параметров указываются внутри круглых скобок через запятую. Если функция не принимает параметров, оставляются только круглые скобки. Не- обходимо также заметить, что количество параметров в определении функции должно сов- падать с количеством параметров при вызове, иначе будет выведено сообщение об ошибке.
Вызвать функции из листинга 11.1 можно способами, указанными в листинге 11.2.
Листинг 11.2. Вызов функций print_ok() # Вызываем функцию без параметров echo("Сообщение") # Функция выведет сообщение x = summa(5, 2) # Переменной x будет присвоено значение 7 a, b = 10, 50 y = summa(a, b) # Переменной y будет присвоено значение 60
Как видно из последнего примера, имя переменной в вызове функции может не совпадать с именем соответствующего параметра в определении функции. Кроме того, глобальные переменные x
и y
не конфликтуют с одноименными переменными, созданными в определе- нии функции, т. к. они расположены в разных областях видимости. Переменные, указанные в определении функции, являются локальными и доступны только внутри функции. Более подробно области видимости мы рассмотрим в разд. 11.9.

212
Часть I. Основы языка Python
Оператор
+
, используемый в функции summa()
, служит не только для сложения чисел, но и позволяет объединить последовательности. То есть функция summa()
может использоваться не только для сложения чисел. В качестве примера выполним конкатенацию строк и объ- единение списков (листинг 11.3).
Листинг 11.3. Многоцелевая функция def summa(x, y): return x + y print(summa("str", "ing")) # Выведет: string print(summa([1, 2], [3, 4])) # Выведет: [1, 2, 3, 4]
Как вы уже знаете, все в языке Python представляет собой объекты: строки, списки и даже сами типы данных. Не являются исключением и функции. Инструкция def создает объект, имеющий тип function
, и сохраняет ссылку на него в идентификаторе, указанном после инструкции def
. Таким образом, мы можем сохранить ссылку на функцию в другой пере- менной — для этого название функции указывается без круглых скобок. Сохраним ссылку в переменной и вызовем функцию через нее (листинг 11.4).
Листинг 11.4. Сохранение ссылки на функцию в переменной def summa(x, y): return x + y f = summa # Сохраняем ссылку в переменной f v = f(10, 20) # Вызываем функцию через переменную f
Можно также передать ссылку на функцию другой функции в качестве параметра (лис- тинг 11.5). Функции, передаваемые по ссылке, обычно называются функциями обратного вызова.
Листинг 11.5. Функции обратного вызова def summa(x, y): return x + y def func(f, a, b):
""" Через переменную f будет доступна ссылка на функцию summa() """ return f(a, b) # Вызываем функцию summa()
# Передаем ссылку на функцию в качестве параметра v = func(summa, 10, 20)
Объекты функций поддерживают множество атрибутов, обратиться к которым можно, ука- зав атрибут после названия функции через точку. Например, через атрибут
__name__
можно получить имя функции в виде строки, через атрибут
__doc__
— строку документирования и т. д. Для примера выведем названия всех атрибутов функции с помощью встроенной функции dir()
:

Глава 11. Пользовательские функции
213
>>> def summa(x, y):
""" Суммирование двух чисел """ return x + y
>>> dir(summa)
['__annotations__', '__call__', '__class__', '__closure__', '__code__',
'__defaults__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__',
'__format__', '__ge__', '__get__', '__getattribute__', '__globals__', '__gt__',
'__hash__', '__init__', '__init_subclass__', '__kwdefaults__', '__le__', '__lt__',
'__module__', '__name__', '__ne__', '__new__', '__qualname__', '__reduce__',
'__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__',
'__subclasshook__']
>>> summa.__name__
'summa'
>>> summa.__code__.co_varnames
('x', 'y')
>>> summa.__doc__
' Суммирование двух чисел '
11.2. Расположение определений функций
Все инструкции в программе выполняются последовательно. Это означает, что прежде чем использовать в программе идентификатор, его необходимо предварительно определить, присвоив ему значение. Поэтому определение функции должно быть расположено перед вызовом функции.
Правильно: def summa(x, y): return x + y v = summa(10, 20) # Вызываем после определения. Все нормально
Неправильно: v = summa(10, 20) # Идентификатор еще не определен. Это ошибка!!! def summa(x, y): return x + y
В последнем случае будет выведено сообщение об ошибке:
NameError: name 'summa' is not defined
. Чтобы избежать ошибки, определение функции размещают в самом начале программы после подключения модулей или в отдельном модуле (о них речь пойдет в главе 12).
С помощью оператора ветвления if можно изменить порядок выполнения программы — например, разместить внутри условия несколько определений функций с одинаковым на- званием, но разной реализацией (листинг 11.6).
Листинг 11.6. Определение функции в зависимости от условия
# -*- coding: utf-8 -*- n = input("Введите 1 для вызова первой функции: ") if n == "1": def echo(): print("Вы ввели число 1")

214
Часть I. Основы языка Python else: def echo(): print("Альтернативная функция") echo() # Вызываем функцию input()
При вводе числа 1 мы получим сообщение "Вы ввели число 1"
, в противном случае —
"Альтернативная функция"
Помните, что инструкция def всего лишь присваивает ссылку на объект функции иденти- фикатору, расположенному после ключевого слова def
. Если определение одной функции встречается в программе несколько раз, будет использоваться функция, которая была опре- делена последней:
>>> def echo(): print("Вы ввели число 1")
>>> def echo(): print("Альтернативная функция")
>>> echo() # Всегда выводит "Альтернативная функция"
11.3. Необязательные параметры и сопоставление по ключам
Чтобы сделать некоторые параметры функции необязательными, следует в определении функции присвоить этому параметру начальное значение. Переделаем функцию суммиро- вания двух чисел и сделаем второй параметр необязательным (листинг 11.7).
Листинг 11.7. Необязательные параметры def summa(x, y=2): # y — необязательный параметр return x + y a = summa(5) # Переменной a будет присвоено значение 7 b = summa(10, 50) # Переменной b будет присвоено значение 60
Таким образом, если второй параметр не задан, он получит значение
2
. Обратите внимание на то, что необязательные параметры должны следовать после обязательных, иначе будет выведено сообщение об ошибке.
До сих пор мы использовали позиционную передачу параметров в функцию: def summa(x, y): return x + y print(summa(10, 20)) # Выведет: 30
Переменной x
при сопоставлении будет присвоено значение
10
, а переменной y
— значе- ние
20
. Но язык Python позволяет также передать значения в функцию, используя сопостав- ление по ключам. Для этого при вызове функции параметрам присваиваются значения, причем последовательность указания параметров в этом случае может быть произвольной
(листинг 11.8).

Глава 11. Пользовательские функции
215
Листинг 11.8. Сопоставление по ключам def summa(x, y): return x + y print(summa(y=20, x=10)) # Сопоставление по ключам
Сопоставление по ключам очень удобно использовать, если функция имеет несколько не- обязательных параметров. В этом случае не нужно указывать все значения, а достаточно присвоить значение нужному параметру:
>>> def summa(a=2, b=3, c=4): # Все параметры являются необязательными return a + b + c
>>> print(summa(2, 3, 20)) # Позиционное присваивание
>>> print(summa(c=20)) # Сопоставление по ключам
Если значения параметров, которые планируется передать в функцию, содержатся в корте- же или списке, то перед этим кортежем или списком следует указать символ
*
. Пример передачи значений из кортежа и списка приведен в листинге 11.9.
Листинг 11.9. Пример передачи значений из кортежа и списка def summa(a, b, c): return a + b + c t1, arr = (1, 2, 3), [1, 2, 3] print(summa(*t1)) # Распаковываем кортеж print(summa(*arr)) # Распаковываем список t2 = (2, 3) print(summa(1, *t2)) # Можно комбинировать значения
Если значения параметров содержатся в словаре, то перед ним следует поставить две звез- дочки:
**
(листинг 11.10).
Листинг 11.10. Пример передачи значений из словаря def summa(a, b, c): return a + b + c d1 = {"a": 1, "b": 2, "c": 3} print(summa(**d1)) # Распаковываем словарь t, d2 = (1, 2), {"c": 3} print(summa(*t, **d2)) # Можно комбинировать значения
Объекты в функцию передаются по ссылке. Если объект относится к неизменяемому типу, то изменение значения внутри функции не затронет значение переменной вне функции:
>>> def func(a, b): a, b = 20, "str"
>>> x, s = 80, "test"
>>> func(x, s) # Значения переменных x и s не изменяются
>>> print(x, s) # Выведет: 80 test
В этом примере значения в переменных x
и s
не изменились. Однако, если объект относится к изменяемому типу, ситуация будет другой:

216
Часть I. Основы языка Python
>>> def func(a, b): a[0], b["a"] = "str", 800
>>> x = [1, 2, 3] # Список
>>> y = {"a": 1, "b": 2} # Словарь
>>> func(x, y) # Значения будут изменены!!!
>>> print(x, y) # Выведет: ['str', 2, 3] {'a': 800, 'b': 2}
Как видно из примера, значения в переменных x
и y
изменились, поскольку список и сло- варь относятся к изменяемым типам. Чтобы избежать изменения значений, внутри функции следует создать копию объекта (листинг 11.11).
Листинг 11.11. Передача изменяемого объекта в функцию def func(a, b): a = a[:] # Создаем поверхностную копию списка b = b.copy() # Создаем поверхностную копию словаря a[0], b["a"] = "str", 800 x = [1, 2, 3] # Список y = {"a": 1, "b": 2} # Словарь func(x, y) # Значения останутся прежними print(x, y) # Выведет: [1, 2, 3] {'a': 1, 'b': 2}
Можно также передать копию объекта непосредственно в вызове функции: func(x[:], y.copy())
Если указать объект, имеющий изменяемый тип, в качестве значения параметра по умолча- нию, этот объект будет сохраняться между вызовами функции:
>>> def func(a=[]): a.append(2) return a
>>> print(func()) # Выведет: [2]
>>> print(func()) # Выведет: [2, 2]
>>> print(func()) # Выведет: [2, 2, 2]
Как видно из примера, значения накапливаются внутри списка. Обойти эту проблему мож- но, например, следующим образом:
>>> def func(a=None):
# Создаем новый список, если значение равно None if a is None: a = [] a.append(2) return a
>>> print(func()) # Выведет: [2]
>>> print(func([1])) # Выведет: [1, 2]
>>> print(func()) # Выведет: [2]
11.4. Переменное число параметров в функции
Если перед параметром в определении функции указать символ
*
, то функции можно будет передать произвольное количество параметров. Все переданные параметры сохраняются

Глава 11. Пользовательские функции
217 в кортеже. Для примера напишем функцию суммирования произвольного количества чисел
(листинг 11.12).
Листинг 11.12. Передача функции произвольного количества параметров def summa(*t):
""" Функция принимает произвольное количество параметров """ res = 0 for i in t: # Перебираем кортеж с переданными параметрами res += i return res print(summa(10, 20)) # Выведет: 30 print(summa(10, 20, 30, 40, 50, 60)) # Выведет: 210
Можно также вначале указать несколько обязательных параметров и параметров, имеющих значения по умолчанию (листинг 11.13).
Листинг 11.13. Функция с параметрами разных типов def summa(x, y=5, *t): # Комбинация параметров res = x + y for i in t: # Перебираем кортеж с переданными параметрами res += i return res print(summa(10)) # Выведет: 15 print(summa(10, 20, 30, 40, 50, 60)) # Выведет: 210
Если перед параметром в определении функции указать две звездочки:
**
, то все именован- ные параметры будут сохранены в словаре (листинг 11.14).
Листинг 11.14. Сохранение переданных данных в словаре def func(**d): for i in d: # Перебираем словарь с переданными параметрами print("{0} => {1}".format(i, d[i]), end=" ") func(a=1, b=2, c=3) # Выведет: a => 1 c => 3 b => 2
При комбинировании параметров параметр с двумя звездочками записывается самым по- следним. Если в определении функции указывается комбинация параметров с одной звез- дочкой и двумя звездочками, то функция примет любые переданные ей параметры (лис- тинг 11.15).
Листинг 11.15. Комбинирование параметров def func(*t, **d):
""" Функция примет любые параметры """ for i in t: print(i, end=" ") for i in d: # Перебираем словарь с переданными параметрами print("{0} => {1}".format(i, d[i]), end=" ")

218
Часть I. Основы языка Python func(35, 10, a=1, b=2, c=3) # Выведет: 35 10 a => 1 c => 3 b => 2 func(10) # Выведет: 10 func(a=1, b=2) # Выведет: a => 1 b => 2
В определении функции можно указать, что некоторые параметры передаются только по именам. Такие параметры должны указываться после параметра с одной звездочкой, но перед параметром с двумя звездочками. Именованные параметры могут иметь значения по умолчанию:
1   ...   16   17   18   19   20   21   22   23   ...   83


написать администратору сайта