План этого занятия

  • Кортежи - tuple
  • Возврат нескольких аргументов из функции
  • Именованные аргументы функции

Кортежи

Кортеж - это неизменяемый и более быстрый аналог списка. Он защищает хранимые данные от непреднамеренных изменений. Называть его можно по-разному: кортеж, тапл, тюпл. Смысл от этого не меняется.

Что означает неизменяемый? То, что после его создания вы не сможете изменить его содержимое.

Посмотрим на примере

>>> list_of_digits = [1, 2, 3]  # обычный список из 3 чисел
>>> list_of_digits[0] = 45  # Если захочется его изменить, то это легко сделать
>>> print(list_of_digits)
[45, 2, 3]

Повторим тот же самый код, но с кортежем

>>> tuple_of_digits = (1, 2, 3,)
>>> tuple_of_digits[0] = 45
TypeError: 'tuple' object does not support item assignment

Ничего не получится.

Синтаксис определения кортежей

>>> empty_tuple = () # Создание кортежа с помощью литерала
>>> yet_another_empty_tuple = tuple() # Создание кортежа с помощью встроенной функции
>>>
>>> some_tuple = (1, 2, ['a', 'b'], 4, 5,) # Инициализация кортежа
>>>
>>> hello_tuple = tuple('hello world') # Создание кортежа из итерируемого объекта, коим является строка
>>> print(hello_tuple)
('h', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd')

При инициализации кортежа лучше завести привычку писать запятую после последнего элемента, иначе есть шанс ошибиться в случае кортежа, состоящего из одного элемента

>>> int_not_tuple = (7) # Это математические скобки, не кортеж
>>> print(type(int_not_tuple))
<class 'int'>
>>> one_element_tuple = (7,) # Кортеж с одним элементом
>>> print(type(one_element_tuple))
<class 'tuple'>

Когда использовать кортежи? Когда нужно что-то неизменяемое. Рассмотрим пример

months = (
    'Январь', 'Февраль', 'Март',
    'Апрель', 'Май', 'Июнь',
    'Июль', 'Август', 'Сентябрь',
    'Октябрь', 'Ноябрь', 'Декабрь'
)

У меня большие сомнения, что состав месяцев года когда-то изменится. Так что выбор тут очевиден - tuple!

Возврат нескольих значений из функции

Tuple очень полезен, если из функции нужно вернуть несколько объектов. До этого мы всегда возвращали из функции только один объект. Например:

def get_first_letter(word):
    return word[0]

А что делать, если нужно вернуть несколько аргументов? В контрольной работе был частотный список слов русского словаря. Он имел вот такой вид:

а|союз|9808.61
абажур|сущ неод ед муж им|7.77
аббревиатура|сущ неод ед жен им|1.47
абвер|сущ неод ед муж им|1.22
аберрация|сущ неод ед жен им|3.0
абзац|сущ неод ед муж им|7.83
абитуриент|сущ одуш ед муж им|1.47
абонемент|сущ неод ед муж им|1.1
абонент|сущ одуш ед муж им|3.43
абориген|сущ одуш ед муж им|10.53
аборт|сущ неод ед муж им|9.79
абразив|сущ неод ед муж им|4.35
абракадабра|сущ неод ед жен им|1.22
...

Можно сильно упростить себе жизнь, если написать функцию, которая будет обрабатывать каждую строчку и сразу возвращать слово, его морфемы и частотность.

def process_line(line):
    word, morph, ipm = line.split('|')
    morphs = morph.split()
    ipm = float(ipm)
    return word, morphs, ipm

И потом пользоваться этой функцией при обработки каждой строки вообще не заботясь о том, какие она операции выполняет.

>>> line = 'абориген|сущ одуш ед муж им|10.53'
>>> process_line(line)
('абориген', ['сущ', 'одуш', 'ед', 'муж', 'им'], 10.53)

Обратите внимание, что можно не ставить скобки в последней строке, питон и так знает, что если перечислено несколько значений через запятую после return, то нужно вернуть кортеж.

Именованные аргументы функции

Мы уже несколько раз сталкивались с именованными аргументами, но не знали, что они так называются.

Все пользовались методом строк split():

>>> line = 'абориген|сущ одуш ед муж им|10.53'
>>> line.split('|', maxsplit=1)
['абориген', 'сущ одуш ед муж им|10.53']

В данном случае maxsplit — это именованный аргумент метода split()

Всеми любимая функция print() имеет сразу несколько именованных аргументов:

  • sep=' '
  • end='\n'
  • и другие

Причем мы видим, что этим аргументам что-то присвоено — используется знак =. На самом деле разработчики языка Python много думали и присвоили этим аргументам именно такие значения чтобы всем было удобно, но их можно задать явно. Например, если в конце строки мы хотим распечатать что-то экзотическое, то можно это сделать:

>>> print('Привет, мир!', end='__end')
Привет, мир!__end
>>>

Если мы хотим при печати нескольих аргументов разделить их не пробелом, а чем-то еще, то можно написать вот так:

>>> print('Привет', 'мой', 'родной', 'город', sep='==')
Привет==мой==родной==город

Как задавать именованные аргументы для своих функций?

>>> def say_hello(name, status='Барон'):
...     print('Привет,', status, name)
...
>>> say_hello('Иван')
Привет, Барон Иван
>>> say_hello('Петр', status='Герцог')
Привет, Герцог Петр

Важно! Именованные аргументы всегда должны опеределяться после позиционных (обычных) аргументов:

>>> def say_hello(status='Барон', name):
...     print('Привет,', status, name)
SyntaxError: non-default argument follows default argument

Нельзя определить именованный аргумент без назначения ему начального значения. Можно только задать специальное значение:

>>> def say_hello(name, status=None):
...     if status is None:
...         print('Привет,', name)
...     else:
...         print('Добрый день,', status, name)
...
>>> say_hello('Петя')
Привет, Петя
>>> say_hello('Катя', status=None)
Привет, Катя
>>> say_hello('Смит', 'миссис')
Добрый день, миссис Смит

Именованные аргументы можно вызывать как позиционные в порядке их определения.

>>> def print_line(symbol, length=10, start='|', end='|'):
...     print(start, symbol*length, end)
>>> print_line('-')
| ---------- |
>>> print_line('-', 20, '[', ']')
[ -------------------- ]

Большинство функций стандартной библиотеки python определены с использованием именованных аргументов, однако часто при вызове той или иной функции име не используется.

Этим нужно пользоваться с осторожностью. Если в функцию передается много параметров (больше 3), то лучше писать имя каждого параметра.

Домашнее задание

Ваша задача сделать универсальную функцию для фильтрации слов в тексте. Функция должна работать для любого нормального текста (текста без пробелов подаваться не будет). Текст возьмите любой, какой хотите. Ваш текст не нужно передавать в виде решения. Решение - это просто функция.

Список фильтров:

  • startswith - строка, с которой должно начинаться слово
  • endswith - строка, на которую должно заканчиваться слово
  • min_length - минимальная длина слова
  • contains - строка, которую обязательно должна быть в слове

Задача будет разбита на несколько вариантов. Каждый вариант реализует только часть фильтров функции. Значения для фильтров будут передаваться в функцию в виде именованных аргументов.

Если функция одного варианта вызывается с аргументами другого варианта, то функция должна выдавать ошибку (про формат ошибки ниже).

Формат возвращаемого значения: неизменяемый список из 3 элементов:

  1. Код успешности. Нужно вернуть 0, если пользователь ввел аргумент, который делает другая команда и 1, если все прошло успешно
  2. Сообщение пользователю. Если произошла ошибка, то помимо кода неуспешности 0 нужно вернуть сообщение об ошибке.
  3. Список отфильтрованных слов. Если ошибка, то просто пустой список.

Скелет программы для работы вот такой:

def words_filter(words, startswith='', min_length =-1, endswith='', contains=''):
    '''Фильтрует слова в соответствии с фильтрами

    words: list
    	Список слов
    startswith: str
    	Строка, с которой должно начинаться слово
    endswith: str
    	Строка, на которую должно заканчиваться слово
    min_length: int
    	Минимальная длина слова
    contains:
    	Строка, которую обязательно должна быть в слове

    Функция возвращает список отфильтрованных слов
    '''
    pass

Как видите, здесь нет функции main и пользовательского ввода через input(). Следует написать просто функцию. Где она будет использоваться - не важно. Ввод может прийти из функции input(), может из файла, может из ввода пользователя на сайте. Ваша задача - это написать функцию. Конечно, при отладке вы должны будете как-то подавать данные на вход функции (например, через input()), но в конечном решении должна быть просто функция.

Распределение фильтров функции по вариантам:

  • 1, 7 - endswith, min_length
  • 2, 3, 4 - contains, startswith
  • 5, 6 - min_length , contains

Как видите у некоторых команд повторяются функции. Как говорится, “Доверяй, но распределяй одни и те же задачи по разным командам, чтобы работа точно была сделана”.

Пример работы с программой для варианта 3 (в данном случае ввод вообще сделан руками в idle):

>>> words = ['машина', 'мама', 'подшипник', 'маршрутка']
>>> filtered_words(words, contains='ш', startswith='ма')
(1, '', ['машина', 'маршрутка'])
>>>
>>> filtered_words(words, contains='ш', min_length=7)
(0, 'Не умею работать с min_length', [])

Выполните задание пройдя по ссылке в GitHub Classroom:


Улучшить эту страницу