Функции в программировании
Содержание:
- Подготавливаем стены
- Примеры поиска максимального значения в последовательности.
- Создание Shadow DOM
- Проверяет, что все элементы в последовательности True.
- Синтаксис функции Python __init __()
- 5 функций для отладки
- Основы
- Функции в Python
- Элементы функционального подохда к программированию
- Карринг
- Использование NumPy
- Аргументы
- Одиночные проверки
- Встроенные типы
- Конструктор классов с наследованием
- Примеры создания и изменения классов «на лету» при помощи type():
- Рекурсия
- С множественным наследованием
- Вызов функции
Подготавливаем стены
Примеры поиска максимального значения в последовательности.
- ;
- ;
- ;
- ;
- ;
- .
# использование позиционных аргументов >>> max(5, 3, 6, 5, 6) # 6 # использование в качестве аргумента - список >>> max() # 5.52 # комбинирование позиционных аргументов и списка # при передаче списка 'x' происходит его распаковка >>> x = (1.2, 1.3, 1.5, 2, 5.52) >>> max(5, 3, 5, *x) # 5,52
Нахождение самой длинной строки в списке строк.
Найдем самую длинную строку. В качестве ключевой функции используем . Она посчитает количество символов в строке каждого элемента списка строк, а функция выберет максимальное число. Строки можно передать например как позиционные аргументы, так и списком , результат будет тот же.
>>> line = 'Jul', 'John', 'Vicky' >>> max(line, key=len) # 'Vicky'
Нахождение в списке строк, записанных как целые числа.
Есть список строк чисел и необходимо найти максимум, как если бы они были целыми числами? Если применить функцию к исходному списку «как есть», то она выберет наибольшее значение списка исходя из лексикографической сортировки. Для нахождения максимума, как числа, применим функцию в качестве ключа , которая «на лету» преобразует элементы списка в целые числа, тогда функция выберет то что нам нужно.
>>> x = '4', '11', '6', '31' >>> max(x) # '6' >>> max(x, key = lambda i int(i)) # '31'
Нахождения в строке, которая состоит из чисел и строк.
Что бы найти максимум в строке, которая состоит из чисел и строк, необходимо сначала разделить исходную строку на список подстрок. Используем приемы, описанные в примерах функции :
- по разделителю, например пробелу или методом строки ,
- вытащить все цифры из исходной строки при помощи функцией .
Затем в цикле перебрать полученный список и все строки с цифрами преобразовать в соответствующие числовые типы и уже потом применить функцию
# исходная строка >>> line = '12; 12,5; 14; один; 15.6; два' # способы преобразования строки в список строк # 1 способ по разделителю ';' >>> line.split(';') # # 2 способ по регулярному выражению >>> import re >>> match = re.findall(r'+', line) >>> list(match) #
Далее будем работать с более сложным списком, полученным 1 способом, где встречаются слова. И так, имеем список строк с цифрами и другими строками. Стоит задача: преобразовать строки с цифрами в соответствующие числовые типы и отбросить строки со словами, что бы потом найти максимум.
Задача усложняется тем, что вещественные числа в строках записаны как через запятую, так и через точку. Для необходимых проверок и преобразований определим функцию .
>>> def str_to_num(str, chars='.', ',']): ... # убираем начальные и конечные пробелы ... str = str.strip() ... if (any(char in str for char in chars) and ... str.replace('.', '').replace(',', '').isdigit()): ... # если в строке есть точка или запятая и при их замене на '' ... # строка состоит только из цифр то это тип float ... return float(str.replace(',', '.')) ... elif str.isdigit(): ... # если строка состоит только из цифр то это тип int ... return int(str) # полученный список строк 1-м способом >>> str_list = '12', ' 12,5', ' 14', ' один', ' 15.6', ' два' # новый список чисел, где будем искать максимум >>> num_list = [] >>> for i in str_list ... # применим функцию преобразования строки в число ... n = str_to_num(i) ... if n is not None ... # если функция возвращает число, ... # то добавляем в новый список ... num_list.append(str_to_num(i)) >>> num_list # >>> max(num_list) # 15.6
Нахождение для ключа или значения в словаре .
Допустим есть словарь, задача: найти максимальное значение ключа или самого значения ключа и вывести эту пару.
# имеем словарь >>> d = {1 3, 2 4, 1 9, 4 1} # преобразуем его в список отображение >>> key_val = d.items() # преобразуем отображение в список # кортежей (ключ, значение) >>> key_val_list = list(key_val) #
По умолчанию, при нахождении максимального элемента из списка кортежей будет выбираться кортеж, у которого наибольшее значение имеет ключ исходного словаря (первый элемент в кортеже).
Но если необходимо получить пару , у которого наибольшее значение имеет значение ключа (второй элемент), то для этого нужно применить лямбда-функцию в качестве аргумента функции , которая укажет, из какого элемента кортежа выбирать наибольшее значение.
# происходит сравнение по # первым элементам кортежа >>> kv = max(key_val_list) >>> kv # (4, 1) # максимальное значение ключа в словаре >>> kv # 4 # меняем порядок сравнения >>> kv = max(key_val_list, key=lambda i i1]) >>> kv # (1, 9) # максимальное значение в словаре >>> kv1 # 9 # ключ этого значения в словаре >>> kv # 1 # получаем максимальное значение из словаря >>> dkv]] # 9
Нахождение списка с наибольшей суммой элементов в списке списков.
# исходный список >>> lst = , 4, 5], 1, 3, 4, 5], 10, 20]] # выбираем список с наибольшей суммой элементов >>> max(lst, key=sum) #
Создание Shadow DOM
Проверяет, что все элементы в последовательности True.
Описание:
Функция возвращает значение , если все элементы в итерируемом объекте — истинны, в противном случае она возвращает значение .
Если передаваемая последовательность пуста, то функция также возвращает .
Функция применяется для проверки на ВСЕХ значений в последовательности и эквивалентна следующему коду:
def all(iterable): for element in iterable if not element return False return True
Так же смотрите встроенную функцию
В основном функция применяется в сочетании с оператором ветвления программы . Работу функции можно сравнить с оператором в Python, только работает с последовательностями:
>>> True and True and True # True >>> True and False and True # False >>> all() # True >>> all() # False
Но между и в Python есть два основных различия:
- Синтаксис.
- Возвращаемое значение.
Функция всегда возвращает или (значение )
>>> all() # True >>> all(]) # False
Если в выражении все значения , то оператор возвращает ПЕРВОЕ истинное значение, а если все значения , то последнее ложное значение. А если в выражении присутствует значение , то ПЕРВОЕ ложное значение. Что бы добиться поведения как у функции , необходимо выражение с оператором обернуть в функцию .
>>> 3 and 1 and 2 and 6 # 6 >>> 3 and and 3 and [] # 0 >>> bool(3 and 1 and 2 and 6) # True >>> bool(3 and and 3 and []) # False
Из всего сказанного можно сделать вывод, что для успешного использования функции необходимо в нее передавать последовательность, полученную в результате каких то вычислений/сравнений, элементы которого будут оцениваться как или . Это можно достичь применяя функцию или выражения-генераторы списков, используя в них встроенные функции или методы, возвращающие значения, операции сравнения, оператор вхождения и оператор идентичности .
num = 1, 2.0, 3.1, 4, 5, 6, 7.9 # использование встроенных функций или # методов на примере 'isdigit()' >>> str(x).isdigit() for x in num # # использование операции сравнения >>> x > 4 for x in num # # использование оператора вхождения `in` >>> '.' in str(x) for x in num # # использование оператора идентичности `is` >>> type(x) is int for x in num # # использование функции map() >>> list(map(lambda x x > 1, num)) False, True, True, True, True, True, True
Примеры проводимых проверок функцией .
Допустим, у нас есть список чисел и для дальнейших операций с этой последовательностью необходимо знать, что все числа например положительные.
>>> num1 = range(1, 9) >>> num2 = range(-1, 7) >>> all() # True >>> all() # False
Или проверить, что последовательность чисел содержит только ЦЕЛЫЕ числа.
>>> num1 = 1, 2, 3, 4, 5, 6, 7 >>> num2 = 1, 2.0, 3.1, 4, 5, 6, 7.9 >>> all() # True >>> all() # False
Или есть строка с числами, записанными через запятую и нам необходимо убедится, что в строке действительно записаны только цифры. Для этого, сначала надо разбить строку на список строк по разделителю и проверить каждый элемент полученного списка на десятичное число методом . Что бы учесть правила записи десятичных чисел будем убирать точку перед проверкой строки на десятичное число.
>>> line1 = "1, 2, 3, 9.9, 15.1, 7" >>> line2 = "1, 2, 3, 9.9, 15.1, 7, девять" >>> all() # True >>> all() # False
Еще пример со строкой. Допустим нам необходимо узнать, есть ли в строке наличие открытой И закрытой скобки?
Синтаксис функции Python __init __()
def __init__(self, )
- Ключевое слово def используется для его определения, потому что это функция.
- Первый аргумент относится к текущему объекту. Он связывает экземпляр с методом init(). Обычно его называют «я», чтобы следовать соглашению об именах. Вы можете узнать больше об этом в собственной переменной Python.
- Аргументы метода init() необязательны. Мы можем определить конструктор с любым количеством аргументов.
Давайте рассмотрим, что мы создаем класс с именем Car. У автомобиля могут быть такие атрибуты, как «цвет», «модель», «скорость» и т. Д., А также такие методы, как «старт», «ускорение», «переключение передач» и т. Д.
class Car(object): def __init__(self, model, color, speed): self.color = color self.speed = speed self.model = model def start(self): print("started") def accelerate(self): print("accelerating...") def change_gear(self, gear_type): print("gear changed")
Поэтому мы использовали метод __init__ конструктора для инициализации атрибутов класса.
5 функций для отладки
Эти функции часто игнорируются, но будут полезны для отладки и устранения неисправностей кода.
breakpoint
Если нужно приостановить выполнение кода и перейти в командную строку Python, эта функция вам пригодится. Вызов перебросит вас в отладчик Python.
Эта встроенная функция была добавлена в Python 3.7, но если вы работаете в более старых версиях, можете получить тот же результат с помощью .
dir
Эта функция может использоваться в двух случаях:
- просмотр списка всех локальных переменных;
- просмотр списка всех атрибутов конкретного объекта.
Из примера можно увидеть локальные переменные сразу после запуска и после создания новой переменной .
Если в передать созданный список , на выходе можно увидеть все его атрибуты.
В выведенном списке атрибутов можно увидеть его типичные методы (, , и т. д.) , а также множество более сложных методов для перегрузки операторов.
vars
Эта функция является своего рода смесью двух похожих инструментов: и .
Когда вызывается без аргументов, это эквивалентно вызову , которая показывает словарь всех локальных переменных и их значений.
Когда вызов происходит с аргументом, получает доступ к атрибуту , который представляет собой словарь всех атрибутов экземпляра.
Перед использованием было бы неплохо сначала обратиться к .
type
Эта функция возвращает тип объекта, который вы ей передаете.
Тип экземпляра класса есть сам класс.
Тип класса — это его метакласс, обычно это .
Атрибут даёт тот же результат, что и функция , но рекомендуется использовать второй вариант.
Функция , кроме отладки, иногда полезна и в реальном коде (особенно в объектно-ориентированном программировании с наследованием и пользовательскими строковыми представлениями).
Обратите внимание, что при проверке типов обычно вместо используется функция. Также стоит понимать, что в Python обычно не принято проверять типы объектов (вместо этого практикуется утиная типизация)
help
Если вы находитесь в Python Shell или делаете отладку кода с использованием , и хотите знать, как работает определённый объект, метод или атрибут, функция поможет вам.
В действительности вы, скорее всего, будете обращаться за помощью к поисковой системе. Но если вы уже находитесь в Python Shell, вызов будет быстрее, чем поиск документации в Google.
Основы
В простейшем случае аннотация содержит непосредственно ожидаемый тип. Более сложные кейсы будут рассмотрены ниже. Если в качестве аннотации указан базовый класс, допустимо передача экземпляров его наследников в качестве значений. Однако использовать можно только те возможности, что реализованы в базовом классе.
Аннотации для переменных пишут через двоеточие после идентификатора. После этого может идти инициализация значения. Например,
Параметры функции аннотируются так же как переменные, а возвращаемое значение указывается после стрелки и до завершающего двоеточия. Например,
Для полей класса аннотации должны быть указаны явно при определении класса. Однако анализаторы могут выводить автоматически их на основе метода, но в этом случае они не будут доступны во время выполнения программы. Подробнее про работу с аннотациями в рантайме во второй части статьи
Кстати, при использовании dataclass типы полей необходимо указывать именно в классе. Подробнее про dataclass
Функции в Python
Функции в Python определяются 2-мя способами: через определение
или через анонимное описание . Оба этих способа
определения доступны, в той или иной степени, и в некоторых других языках программирования. Особенностью
Python является то, что функция является таким же именованным объектом, как и любой другой объект некоторого
типа данных, скажем, как целочисленная переменная. В листинге 1 представлен простейший пример (файл
func.py из архива python_functional.tgz в разделе «Материалы для скачивания»):
Листинг 1. Определения функций
#!/usr/bin/python # -*- coding: utf-8 -*- import sys def show( fun, arg ): print( '{} : {}'.format( type( fun ), fun ) ) print( 'arg={} => fun( arg )={}'.format( arg, fun( arg ) ) ) if len( sys.argv ) > 1: n = float( sys.argv ) else: n = float( input( "число?: " ) ) def pow3( n ): # 1-е определение функции return n * n * n show( pow3, n ) pow3 = lambda n: n * n * n # 2-е определение функции с тем же именем show( pow3, n ) show( ( lambda n: n * n * n ), n ) # 3-е, использование анонимного описание функции
При вызове всех трёх объектов-функций мы получим один и тот же результат:
$ python func.py 1.3 <type 'function'> : <function pow3 at 0xb7662844> arg=1.3 => fun( arg )=2.197 <type 'function'> : <function <lambda> at 0xb7662bc4> arg=1.3 => fun( arg )=2.197 <type 'function'> : <function <lambda> at 0xb7662844> arg=1.3 => fun( arg )=2.197
Ещё более отчётливо это проявляется в Python версии 3, в которой всё является классами (в том числе, и
целочисленная переменная), а функции являются объектами программы, принадлежащими к классу
:
$ python3 func.py 1.3 <class 'function'> : <function pow3 at 0xb74542ac> arg=1.3 => fun( arg )=2.1970000000000005 <class 'function'> : <function <lambda> at 0xb745432c> arg=1.3 => fun( arg )=2.1970000000000005 <class 'function'> : <function <lambda> at 0xb74542ec> arg=1.3 => fun( arg )=2.1970000000000005
Примечание. Существуют ещё 2 типа объектов, допускающих функциональный вызов —
функциональный метод класса и функтор, о которых мы поговорим позже.
Если функциональные объекты Python являются такими же объектами, как и другие объекты данных, значит,
с ними можно и делать всё то, что можно делать с любыми данными:
- динамически изменять в ходе выполнения;
- встраивать в более сложные структуры данных (коллекции);
- передавать в качестве параметров и возвращаемых значений и т.д.
На этом (манипуляции с функциональными объектами как с объектами данных) и базируется функциональное
программирование. Python, конечно, не является настоящим языком функционального программирования, так,
для полностью функционального программирования существуют специальные языки: Lisp, Planner, а из более
свежих: Scala, Haskell. Ocaml, … Но в Python можно «встраивать» приёмы функционального программирования в
общий поток императивного (командного) кода, например, использовать методы, заимствованные из
полноценных функциональных языков. Т.е. «сворачивать» отдельные фрагменты императивного кода (иногда
достаточно большого объёма) в функциональные выражения.
Временами спрашивают: «В чём преимущества функционального стиля написания отдельных фрагментов
для программиста?». Основным преимуществом функционального программирования является то, что после
однократной отладки такого фрагмента в нём при последующем многократном использовании не возникнут
ошибки за счёт побочных эффектов, связанных с присвоениями и конфликтом имён.
Достаточно часто при программировании на Python используют типичные конструкции из области
функционального программирования, например:
print ( )
В результате запуска получаем:
$ python funcp.py
Элементы функционального подохда к программированию
При написании
программ приветствуется такой подход, который называется функциональным
программированием. Продемонстрирую его на следующем примере. Предположим,
нам нужна функция, которая находит максимальное значение из двух чисел:
def max2(a, b): if a > b: return a return b
И вызвать мы ее
можем так:
print( max2(2, -3) )
Затем, нам
потребовалась функция, которая бы находила максимальное из трех чисел. Как ее
можно реализовать? Используя идею функционального программирования, это можно
сделать следующим образом:
def max3(a, b, c): return max2(a, max2(b, c))
И вызвать так:
print( max3(2, -3, 5) )
Смотрите, здесь
оператор return возвращает
значение, которое возвращает функция max2. Но, прежде чем она будет
выполнена, вызовется другая функция max2, которая определит максимальное
среди чисел b и c. То есть,
прежде чем вызвать первую функцию max2 необходимо вычислить ее
параметры: первый просто берется их x, а второй вычисляется вложенной
функцией max2. Вот так это
работает и вот что из себя представляет элемент функционального подхода к
программированию.
Причем,
благодаря гибкости языка Python, мы можем вызвать эту функцию и
для нахождения максимальной строки:
print( max3("ab", "cd", "abc") )
так как строки
могут спокойно сравниваться между собой. И вообще, любые величины, которые
можно сравнивать на больше и меньше, можно подставлять в качестве аргументов
функции max3 и max2.
Карринг
Карринг (или каррирование, curring) — преобразование функции от многих переменных в функцию, берущую
свои аргументы по одному.
Примечание. Это преобразование было введено М. Шейнфинкелем и Г. Фреге и получило
своё название в честь математика Хаскелла Карри, в честь которого также назван и язык программирования
Haskell.
Карринг не относится к уникальным особенностям функционального программирования, так карринговое
преобразование может быть записано, например, и на языках Perl или C++. Оператор каррирования даже
встроен в некоторые языки программирования (ML, Haskell), что позволяет многоместные функции приводить к
каррированному представлению. Но все языки, поддерживающие замыкания, позволяют записывать
каррированные функции, и Python не является исключением в этом плане.
В листинге 8 представлен простейший пример с использованием карринга (файл curry1.py
в архиве python_functional.tgz в разделе «Материалы для скачивания»):
Листинг 8. Карринг
# -*- coding: utf-8 -*- def spam( x, y ): print( 'param1={}, param2={}'.format( x, y ) ) spam1 = lambda x : lambda y : spam( x, y ) def spam2( x ) : def new_spam( y ) : return spam( x, y ) return new_spam spam1( 2 )( 3 ) # карринг spam2( 2 )( 3 )
Вот как выглядят исполнение этих вызовов:
$ python curry1.py param1=2, param2=3 param1=2, param2=3
Использование NumPy
NumPy – это сторонняя библиотека Python. Если вы собираетесь ее использовать, сначала вам нужно убедиться в том, что она установлена.
Как это сделать при помощи REPL:
Python
import numpy
1 | importnumpy |
Если вы получите ошибку , то вам нужно провести установку numpy. Чтобы сделать это, перейдите в командную строку и введите:
Python
pip install numpy
1 | pip install numpy |
После установки, внесите следующее:
Python
import numpy as np
np.arange(0.3, 1.6, 0.3)
1 |
importnumpy asnp np.arange(0.3,1.6,0.3) |
Результат:
Python
array()
1 | array(0.3,0.6,0.9,1.2,1.5) |
Если вы хотите вывести каждое число на свою строку, вы можете сделать следующее:
Python
import numpy as np
for i in np.arange(0.3, 1.6, 0.3):
print(i)
1 |
importnumpy asnp foriinnp.arange(0.3,1.6,0.3) print(i) |
Выдача будет следующей:
Python
0.3
0.6
0.8999999999999999
1.2
1.5
1 |
0.3 0.6 0.8999999999999999 1.2 1.5 |
Но откуда взялось число 0.8999999999999999?
У компьютеров есть проблемы с сохранением десятичных чисел с запятой в двоичные числа с запятой. Это приводит к разным неожиданным представлениям этих чисел.
Так или иначе, эти ошибки связанные с плавающей запятой являются проблемой, в зависимости от того, над какой задачей вы работаете. Ошибки могут быть выражены в виде, например, шестнадцатеричного десятичного числа, что не является критичной проблемой, в большинстве случаев. Они настолько маленькие, что, если вы только не работаете над расчетами орбитальной траектории спутников, вам не стоит беспокоиться.
В качестве альтернативы, вы можете использовать np.linspace(). Он делает в целом то же самое, но с использованием других параметров. С np.linspace() вы определяете начало и конец (оба включительно), а также длину и массив (за исключением шага).
Например, np.linspace(1, 4, 20) выдает 20 одинаково разделенных чисел: .0, …, 4.0. В другом случае, np.linspace(0, 0.5, 51) задает 0.00, 0.01, 0.02, 0.03, …, 0.49, 0.50.
Аргументы
Позиционные
Вспомним, аргумент – это конкретное значение, которое передаётся в функцию. Аргументом может быть любой объект. Он может передаваться, как в литеральной форме, так и в виде переменной.
Значения в позиционных аргументах подставляются согласно позиции имён аргументов:
Именованные
Пусть есть функция, принимающая три аргумента, а затем выводящая их на экран. Python позволяет явно задавать соответствия между значениями и именами аргументов.
При вызове соответствие будет определяться по именам, а не по позициям аргументов:
Необязательные параметры (параметры по умолчанию)
Python позволяет делать отдельные параметры функции необязательными. Если при вызове значение такого аргумента не передается, то ему будет присвоено значение по умолчанию.
Аргументы переменной длины (args, kwargs)
Когда заранее неизвестно, сколько конкретно аргументов будет передано в функцию, мы пользуемся аргументами переменной длины. Звёздочка «*» перед именем параметра сообщает интерпретатору о том, что количество позиционных аргументов будет переменным:
Переменная составляет кортеж из переданных в функцию аргументов.
Функции в питоне могут также принимать и переменное количество именованных аргументов. В этом случае перед названием параметра ставится «»:
Здесь kwargs уже заключает аргументы не в кортеж, а в словарь.
Если объект неизменяемый, то он передаётся в функцию по значению. Неизменяемые объекты это:
- Числовые типы (int, float, complex).
- Строки (str).
- Кортежи (tuple).
В Python изменяемые объекты это:
- Списки (list).
- Множества (set).
- Словари (dict).
Будьте внимательны при передаче изменяемых объектов. Одна из частых проблем новичков.
В функциональном программировании существует понятие «функциями с побочными эффектами» – когда функция в процессе своей работы изменяет значения глобальных переменных. По возможности, избегать таких функций.
Одиночные проверки
Внутри условия
можно прописывать и такие одиночные выражения:
x = 4; y = True; z = False if(x): print("x = ", x, " дает true") if(not ): print("0 дает false") if("0"): print("строка 0 дает true") if(not ""): print("пустая строка дает false") if(y): print("y = true дает true") if(not z): print("z = false дает false")
Вот этот оператор
not – это отрицание
– НЕ, то есть, чтобы проверить, что 0 – это false мы
преобразовываем его в противоположное состояние с помощью оператора отрицания
НЕ в true и условие
срабатывает. Аналогично и с переменной z, которая равна false.
Из этих примеров
можно сделать такие выводы:
-
Любое число,
отличное от нуля, дает True. Число 0 преобразуется в False. -
Пустая строка –
это False, любая другая
строка с символами – это True. - С помощью
оператора not можно менять
условие на противоположное (в частности, False превращать в True).
Итак, в условиях
мы можем использовать три оператора: and, or и not. Самый высокий
приоритет у операции not, следующий приоритет имеет операция and и самый
маленький приоритет у операции or. Вот так работает оператор if в Python.
Видео по теме
Python 3 #1: установка и запуск интерпретатора языка
Python 3 #2: переменные, оператор присваивания, типы данных
Python 3 #3: функции input и print ввода/вывода
Python 3 #4: арифметические операторы: сложение, вычитание, умножение, деление, степень
Python 3 #5: условный оператор if, составные условия с and, or, not
Python 3 #6: операторы циклов while и for, операторы break и continue
Python 3 #7: строки — сравнения, срезы строк, базовые функции str, len, ord, in
Python 3 #8: методы строк — upper, split, join, find, strip, isalpha, isdigit и другие
Python 3 #9: списки list и функции len, min, max, sum, sorted
Python 3 #10: списки — срезы и методы: append, insert, pop, sort, index, count, reverse, clear
Python 3 #11: списки — инструмент list comprehensions, сортировка методом выбора
Python 3 #12: словарь, методы словарей: len, clear, get, setdefault, pop
Python 3 #13: кортежи (tuple) и операции с ними: len, del, count, index
Python 3 #14: функции (def) — объявление и вызов
Python 3 #15: делаем «Сапер», проектирование программ «сверху-вниз»
Python 3 #16: рекурсивные и лямбда-функции, функции с произвольным числом аргументов
Python 3 #17: алгоритм Евклида, принцип тестирования программ
Python 3 #18: области видимости переменных — global, nonlocal
Python 3 #19: множества (set) и операции над ними: вычитание, пересечение, объединение, сравнение
Python 3 #20: итераторы, выражения-генераторы, функции-генераторы, оператор yield
Python 3 #21: функции map, filter, zip
Python 3 #22: сортировка sort() и sorted(), сортировка по ключам
Python 3 #23: обработка исключений: try, except, finally, else
Python 3 #24: файлы — чтение и запись: open, read, write, seek, readline, dump, load, pickle
Python 3 #25: форматирование строк: метод format и F-строки
Python 3 #26: создание и импорт модулей — import, from, as, dir, reload
Python 3 #27: пакеты (package) — создание, импорт, установка (менеджер pip)
Python 3 #28: декораторы функций и замыкания
Python 3 #29: установка и порядок работы в PyCharm
Python 3 #30: функция enumerate, примеры использования
Встроенные типы
Хоть вы и можете использовать стандартные типы в качестве аннотаций, много полезного сокрыто в модуле .
Optional
Если вы пометите переменную типом и попытаетесь присвоить ей , будет ошибка:
Для таких случаев предусмотрена в модуле typing аннотация с указанием конкретного типа
Обратите внимание, тип опциональной переменной указывается в квадратных скобках
Any
Иногда вы не хотите ограничивать возможные типы переменной
Например, если это действительно не важно, или если вы планируете сделать обработку разных типов самостоятельно. В этом случае, можно использовать аннотацию
На следующий код mypy не будет ругаться:
Может возникнуть вопрос, почему не использовать ? Однако в этом случае предполагается, что хоть передан может быть любой объект, обращаться с ним можно только как с экземпляром .
Union
Для случаев, когда необходимо допустить использование не любых типов, а только некоторых, можно использовать аннотацию с указанием списка типов в квадратных скобках.
Кстати, аннотация эквивалентна , хотя такая запись и не рекомендуется.
Коллекции
Механизм аннотаций типов поддерживает механизм дженериков (, подробнее во второй части статьи), которые позволяют специфицировать для контейнеров типы элементов, хранящихся в них.
Списки
Для того, чтобы указать, что переменная содержит список можно использовать тип list в качестве аннотации. Однако если хочется конкретизировать, какие элементы содержит список, он такая аннотация уже не подойдёт. Для этого есть . Аналогично тому, как мы указывали тип опциональной переменной, мы указываем тип элементов списка в квадратных скобках.
Предполагается, что список содержит неопределенное количество однотипных элементов. Но при этом нет ограничений на аннотацию элемента: можно использовать , , и другие. Если тип элемента не указан, предполагается, что это .
Кроме списка аналогичные аннотации есть для множеств: и .
Кортежи
Кортежи в отличие от списков часто используются для разнотипных элементов. Синтаксис похож с одним отличием: в квадратных скобках указывается тип каждого элемента кортежа по отдельности.
Если же планируется использовать кортеж аналогично списку: хранить неизвестное количество однотипных элементов, можно воспользоваться многоточием ().
Аннотация без указания типов элементов работает аналогично
Словари
Для словарей используется . Отдельно аннотируется тип ключа и тип значений:
Аналогично используются и
Результат выполнения функции
Для указания типа результата функции можно использовать любую аннотацию. Но есть несколько особенных случаев.
Если функция ничего не возвращает (например, как ), её результат всегда равен . Для аннотации так же используем .
Корректными вариантами завершения такой функции будут: явный возврат , возврат без указания значения и завершение без вызова .
Если же функция никогда не возвращает управление (например, как ), следует использовать аннотацию :
Если это генераторная функция, то есть её тело содержит оператор , для возвращаемого можно воспользоватьтся аннотацией , либо :
Конструктор классов с наследованием
class Person: def __init__(self, n): print('Person Constructor') self.name = n class Employee(Person): def __init__(self, i, n): print('Employee Constructor') super().__init__(n) # same as Person.__init__(self, n) self.id = i emp = Employee(99, 'Pankaj') print(f'Employee ID is {emp.id} and Name is {emp.name}')
Выход:
Employee Constructor Person Constructor Employee ID is 99 and Name is Pankaj
- Мы обязаны вызвать конструктор суперкласса.
- Мы можем использовать функцию super() для вызова функции конструктора суперкласса.
- Мы также можем использовать имя суперкласса для вызова его метода init().
Примеры создания и изменения классов «на лету» при помощи type():
Например, следующие два определения создают идентичные объекты.
>>> class Foo(object): ... bar = True >>> Foo = type('Foo', (), {'bar'True})
Класс , созданный через метакласс можно использовать как обычный класс:
>>> Foo # <class '__main__.Foo'> >>> f = Foo() >>> f.bar # True # можно наследоваться от него >>> class FooChild(Foo): pass ... >>> FooChild # <class '__main__.FooChild'> >>> FooChild.bar # bar унаследован от Foo # True
Добавим методы в класс . Для этого определим функцию и добавим ее как атрибут.
>>> def echo_bar(self): ... print(self.bar) ... >>> FooChild = type('FooChild', (Foo,), {'echo_bar' echo_bar}) >>> hasattr(Foo, 'echo_bar') # False >>> hasattr(FooChild, 'echo_bar') # True >>> my_foo = FooChild() >>> my_foo.echo_bar() # True # после динамического создания класса добавим еще один метод >>> def echo_bar_more(self): ... print('yet another method') ... >>> FooChild.echo_bar_more = echo_bar_more >>> hasattr(FooChild, 'echo_bar_more') # True
Рекурсия
В функциональном программировании рекурсия является основным механизмом, аналогично циклам в
итеративном программировании.
В некоторых обсуждениях по Python неоднократно приходилось встречаться с заявлениями, что в Python
глубина рекурсии ограничена «аппаратно», и поэтому некоторые действия реализовать невозможно в принципе.
В интерпретаторе Python действительно по умолчанию установлено ограничение глубины рекурсии, равным
1000, но это численный параметр, который всегда можно переустановить, как показано в листинге 3 (полный код
примера можно найти в файле fact2.py из архива python_functional.tgz в
разделе «Материалы для скачивания»):
Листинг 3. Вычисление факториала с произвольной глубиной рекурсии
#!/usr/bin/python # -*- coding: utf-8 -*- import sys arg = lambda : ( len( sys.argv ) > 1 and int( sys.argv ) ) or \ int( input( "число?: " ) ) factorial = lambda x: ( ( x == 1 ) and 1 ) or x * factorial( x - 1 ) n = arg() m = sys.getrecursionlimit() if n >= m - 1 : sys.setrecursionlimit( n + 2 ) print( "глубина рекурсии превышает установленную в системе {}, переустановлено в {}".\ format( m, sys.getrecursionlimit() ) ) print( "n={} => n!={}".format( n, factorial( n ) ) ) if sys.getrecursionlimit() > m : print( "глубина рекурсии восстановлена в {}".format( m ) ) sys.setrecursionlimit( m )
Вот как выглядит исполнение этого примера в Python 3 и в Python2 (правда на самом деле полученное
число вряд ли поместится на один экран терминала консоли):
$ python3 fact2.py 1001 глубина рекурсии превышает установленную в системе 1000, переустановлено в 1003 n=1001 => n!=4027.................................................0000000000000 глубина рекурсии восстановлена в 1000
С множественным наследованием
Мы не можем использовать super() для доступа ко всем суперклассам в случае множественного наследования. Лучшим подходом было бы вызвать функцию конструктора суперклассов, используя их имя класса.
class A1: def __init__(self, a1): print('A1 Constructor') self.var_a1 = a1 class B1: def __init__(self, b1): print('B1 Constructor') self.var_b1 = b1 class C1(A1, B1): def __init__(self, a1, b1, c1): print('C1 Constructor') A1.__init__(self, a1) B1.__init__(self, b1) self.var_c1 = c1 c_obj = C1(1, 2, 3) print(f'c_obj var_a={c_obj.var_a1}, var_b={c_obj.var_b1}, var_c={c_obj.var_c1}')
Вывод:
C1 Constructor A1 Constructor B1 Constructor c_obj var_a=1, var_b=2, var_c=3
Python не поддерживает несколько конструкторов
Python не поддерживает несколько конструкторов, в отличие от других популярных объектно-ориентированных языков программирования, таких как Java.
Мы можем определить несколько методов __init __(), но последний из них переопределит предыдущие определения.
class D: def __init__(self, x): print(f'Constructor 1 with argument {x}') # this will overwrite the above constructor definition def __init__(self, x, y): print(f'Constructor 1 with arguments {x}, {y}') d1 = D(10, 20) # Constructor 1 with arguments 10, 20
Может ли функция Python __init __() что-то вернуть?
Если мы попытаемся вернуть значение, отличное от None, из функции __init __(), это вызовет ошибку TypeError.
class Data: def __init__(self, i): self.id = i return True d = Data(10)
Вывод:
TypeError: __init__() should return None, not 'bool'
Если мы изменим оператор return на тогда код будет работать без каких-либо исключений.
Вызов функции
Рассмотрим полную версию программы с функцией:
def countFood(): a = int(input()) b = int(input()) print("Всего", a+b, "шт.") print("Сколько бананов и ананасов для обезьян?") countFood() print("Сколько жуков и червей для ежей?") countFood() print("Сколько рыб и моллюсков для выдр?") countFood()
После вывода на экран каждого информационного сообщения осуществляется вызов функции, который выглядит просто как упоминание ее имени со скобками. Поскольку в функцию мы ничего не передаем скобки опять же пустые. В приведенном коде функция вызывается три раза.
Когда функция вызывается, поток выполнения программы переходит к ее определению и начинает исполнять ее тело. После того, как тело функции исполнено, поток выполнения возвращается в основной код в то место, где функция вызывалась. Далее исполняется следующее за вызовом выражение.
В языке Python определение функции должно предшествовать ее вызовам. Это связано с тем, что интерпретатор читает код строка за строкой и о том, что находится ниже по течению, ему еще неизвестно. Поэтому если вызов функции предшествует ее определению, то возникает ошибка (выбрасывается исключение NameError):
print("Сколько бананов и ананасов для обезьян?") countFood() print("Сколько жуков и червей для ежей?") countFood() print("Сколько рыб и моллюсков для выдр?") countFood() def countFood(): a = int(input()) b = int(input()) print("Всего", a+b, "шт.")
Результат:
Сколько бананов и ананасов для обезьян? Traceback (most recent call last): File "test.py", line 2, in <module> countFood() NameError: name 'countFood' is not defined
Для многих компилируемых языков это не обязательное условие. Там можно определять и вызывать функцию в произвольных местах программы. Однако для удобочитаемости кода программисты даже в этом случае предпочитают соблюдать определенные правила.