ээдд. Прохоренок_Н_А__Дронов_В_А_Python_3_и_PyQt_5_Разработка_приложен. Николай Прохоренок Владимир Дронов
Скачать 7.92 Mb.
|
. Например, в параметре 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 В определении функции можно указать, что некоторые параметры передаются только по именам. Такие параметры должны указываться после параметра с одной звездочкой, но перед параметром с двумя звездочками. Именованные параметры могут иметь значения по умолчанию: |