37. Встроенные функции и полезные инструменты
Python предоставляет богатую коллекцию встроенных функций, которые всегда доступны без необходимости импортировать какие-либо модули. Эти функции составляют основу повседневного программирования на Python, помогая эффективно работать с данными, последовательностями и коллекциями. В этой главе мы рассмотрим самые полезные встроенные инструменты Python и научимся использовать их, чтобы писать более чистый и выразительный код.
Понимание системы типов Python
Прежде чем перейти к конкретным встроенным функциям, полезно понять, как Python организует свои типы данных. Эти знания помогут вам предсказать, какие операции работают с какими типами, и понимать сообщения об ошибках, когда они возникают.
Типы данных Python можно понять с двух дополняющих друг друга точек зрения:
Иерархия типов: как типы связаны между собой
Это показывает, как Python организует типы в семейства на основе того, чем они ЯВЛЯЮТСЯ.
Представление по возможностям: что типы умеют делать
Для встроенных функций важнее то, что типы могут ДЕЛАТЬ:
Ключевые возможности:
- Итерируемый(iterable): можно использовать в циклах
for→ работает сsum(),any(),all(),sorted() - Коллекция(collection): итерируемый с
len()→ работает сlen()и операторомin - Последовательность(sequence): коллекция с индексацией → поддерживает
[index]и срезы[start:end]
Почему это важно
Встроенные функции требуют определённых возможностей:
| Функция | Требует | Работает с |
|---|---|---|
len() | Коллекция | str, list, dict, set, tuple |
sum() | Итерируемый из чисел | list, tuple, set, range, generator |
sorted() | Итерируемый | str, list, dict, set, tuple |
[index] | Последовательность | str, list, tuple, range |
Понимание этих категорий помогает вам:
- Предсказывать, какие функции работают с какими типами
- Понимать сообщения об ошибках вроде "object is not iterable"
- Знать, когда можно индексировать (
[0]), а когда можно только перебирать (for)
37.1) Распространённые встроенные функции (len, sum, min, max, abs, round)
Самые часто используемые встроенные функции Python помогают выполнять типовые операции с данными без написания циклов или сложной логики. Эти функции оптимизированы, читаемы и составляют основу «питоничного» кода.
37.1.1) Измерение длины с помощью len()
Функция len() возвращает количество элементов в коллекции. Она работает со строками, списками, кортежами, словарями, множествами и любыми другими типами коллекций.
# Подсчёт символов в строке
message = "Hello, World!"
print(len(message)) # Output: 13
# Подсчёт элементов в списке
scores = [85, 92, 78, 90, 88]
print(len(scores)) # Output: 5
# Подсчёт пар ключ-значение в словаре
student = {"name": "Bob", "age": 21, "major": "CS"}
print(len(student)) # Output: 3
# Подсчёт уникальных элементов в множестве
unique_ids = {101, 102, 103, 101, 102} # Дубликаты удаляются
print(len(unique_ids)) # Output: 3Функция len() особенно полезна, когда нужно узнать размер данных перед их обработкой:
# Обработка данных в зависимости от размера
data = [12, 45, 23, 67, 89, 34]
if len(data) < 5:
print("Not enough data for analysis")
else:
print(f"Analyzing {len(data)} data points") # Output: Analyzing 6 data points
average = sum(data) / len(data)
print(f"Average: {average}") # Output: Average: 45.037.1.2) Подсчёт суммы с помощью sum()
Функция sum() складывает все числа в итерируемом объекте. Это гораздо чище, чем писать цикл для накопления значений.
# Суммирование списка чисел
prices = [19.99, 24.50, 15.75, 32.00]
total = sum(prices)
print(f"Total: ${total}") # Output: Total: $92.24
# Суммирование кортежа
daily_steps = (8500, 10200, 7800, 9500, 11000)
weekly_total = sum(daily_steps)
print(f"Total steps this week: {weekly_total}") # Output: Total steps this week: 47000
# Суммирование range
total_1_to_100 = sum(range(1, 101))
print(total_1_to_100) # Output: 5050Практический пример, объединяющий sum() и len() для вычисления среднего:
# Вычислить средний балл теста
test_scores = [88, 92, 79, 85, 90, 87]
total_score = sum(test_scores)
num_tests = len(test_scores)
average_score = total_score / num_tests
print(f"Average score: {average_score:.1f}") # Output: Average score: 86.8Важное ограничение: sum() работает только с числами. Вы не можете использовать её для конкатенации строк или объединения списков:
# Это вызовет TypeError
words = ["Hello", " ", "World"]
# sentence = sum(words) # TypeError: unsupported operand type(s)37.1.3) Поиск экстремумов с помощью min() и max()
Функции min() и max() находят наименьшее и наибольшее значения в итерируемом объекте. Они работают с числами, строками и любыми объектами, которые можно сравнивать.
# Нахождение минимальной и максимальной температуры
temperatures = [72, 68, 75, 70, 73, 69]
coldest = min(temperatures)
warmest = max(temperatures)
print(f"Temperature range: {coldest}°F to {warmest}°F")
# Output: Temperature range: 68°F to 75°F
# Нахождение минимальной и максимальной строки (по алфавиту)
names = ["Zoe", "Alice", "Bob", "Charlie"]
first_alphabetically = min(names)
last_alphabetically = max(names)
print(f"First: {first_alphabetically}, Last: {last_alphabetically}")
# Output: First: Alice, Last: ZoeМожно также передавать несколько аргументов напрямую, а не коллекцию:
# Сравнение отдельных значений
lowest = min(45, 23, 67, 12, 89)
highest = max(45, 23, 67, 12, 89)
print(f"Lowest: {lowest}, Highest: {highest}")
# Output: Lowest: 12, Highest: 89
# Полезно для сравнения нескольких конкретных значений
price1 = 19.99
price2 = 24.50
price3 = 15.75
cheapest = min(price1, price2, price3)
print(f"Cheapest option: ${cheapest}") # Output: Cheapest option: $15.7537.1.4) Получение абсолютного значения с помощью abs()
Функция abs() возвращает абсолютное значение числа — расстояние от нуля, всегда положительное. Это полезно, когда важна величина, но не направление.
# Абсолютное значение отрицательных чисел
print(abs(-42)) # Output: 42
print(abs(-3.14)) # Output: 3.14
# Абсолютное значение положительных чисел (без изменений)
print(abs(42)) # Output: 42
print(abs(3.14)) # Output: 3.14
# Абсолютное значение нуля
print(abs(0)) # Output: 0Распространённый сценарий — вычисление разницы, где направление не важно:
# Вычислить изменение температуры (только величина)
morning_temp = 65
evening_temp = 72
temperature_change = abs(evening_temp - morning_temp)
print(f"Temperature changed by {temperature_change}°F")
# Output: Temperature changed by 7°F37.1.5) Округление чисел с помощью round()
Функция round() округляет число до заданного количества знаков после запятой. Без второго аргумента она округляет до ближайшего целого.
# Округление до ближайшего целого
print(round(3.7)) # Output: 4
print(round(3.2)) # Output: 3
print(round(3.5)) # Output: 4
print(round(4.5)) # Output: 4 (округляет до ближайшего чётного числа)
# Округление до заданного количества знаков после запятой
price = 19.876
print(round(price, 2)) # Output: 19.88 (2 знака после запятой)
print(round(price, 1)) # Output: 19.9 (1 знак после запятой)
# Округление до отрицательного количества знаков (до десятков, сотен и т. д.)
population = 1234567
print(round(population, -3)) # Output: 1235000 (до ближайшей тысячи)
print(round(population, -4)) # Output: 1230000 (до ближайших десяти тысяч)Примечание о значениях ровно посередине: При округлении числа, которое находится ровно посередине между двумя целыми, в Python действует особое правило. Например, 2.5 находится ровно посередине между 2 и 3. Можно ожидать округления вверх до 3, но Python округляет к ближайшему чётному соседу — в этом случае это 2.
Это называется «банковское округление» или «округление половины к чётному». Это часть стандарта IEEE 754 и помогает уменьшить смещение при большом количестве операций округления.
# Значения посередине округляются к ближайшему чётному числу
print(round(0.5)) # Output: 0 (0 is even)
print(round(1.5)) # Output: 2 (2 is even)
print(round(2.5)) # Output: 2 (2 is even)
print(round(3.5)) # Output: 4 (4 is even)
print(round(4.5)) # Output: 4 (4 is even)37.2) Перечисление последовательностей с помощью enumerate()
При обходе последовательности часто нужны и элемент, и его позиция. Функция enumerate() предоставляет и то и другое, устраняя необходимость в ручных переменных-счётчиках.
37.2.1) Проблема ручных счётчиков
До знакомства с enumerate() программисты часто используют переменную-счётчик для отслеживания позиции:
# Подход с ручным счётчиком (работает, но не идеален)
fruits = ["apple", "banana", "cherry", "date"]
index = 0
for fruit in fruits:
print(f"{index}: {fruit}")
index += 1
# Output:
# 0: apple
# 1: banana
# 2: cherry
# 3: dateУ этого подхода есть несколько недостатков:
- Лишняя переменная, которой нужно управлять (
index) - Легко забыть увеличить счётчик
37.2.2) Использование enumerate() для позиции и значения
Функция enumerate() решает эту проблему элегантно. Она принимает итерируемый объект и возвращает пары (index, element):
# Использование enumerate() — чище и более «по-питоничному»
fruits = ["apple", "banana", "cherry", "date"]
for index, fruit in enumerate(fruits):
print(f"{index}: {fruit}")
# Output:
# 0: apple
# 1: banana
# 2: cherry
# 3: dateСинтаксис for index, fruit in enumerate(fruits) использует распаковку кортежа(tuple unpacking) (как мы изучали в главе 15). На каждой итерации enumerate() даёт кортеж вроде (0, "apple"), который распаковывается в переменные index и fruit.
Вот что фактически возвращает enumerate():
# Просмотр вывода enumerate() напрямую
fruits = ["apple", "banana", "cherry"]
enumerated = list(enumerate(fruits))
print(enumerated)
# Output: [(0, 'apple'), (1, 'banana'), (2, 'cherry')]37.2.3) Начало перечисления с другого числа
По умолчанию enumerate() начинает счёт с 0. Вы можете задать другое начальное число с параметром start:
# Начать счёт с 1 (удобно для отображения)
tasks = ["Write code", "Test code", "Deploy code"]
for number, task in enumerate(tasks, start=1):
print(f"Step {number}: {task}")
# Output:
# Step 1: Write code
# Step 2: Test code
# Step 3: Deploy codeЭто особенно полезно при показе пользователям нумерованных списков, где обычно ожидают, что счёт начнётся с 1:
# Меню с пронумерованными пунктами
menu_items = ["New Game", "Load Game", "Settings", "Quit"]
print("Main Menu:")
for number, item in enumerate(menu_items, start=1):
print(f"{number}. {item}")
# Output:
# Main Menu:
# 1. New Game
# 2. Load Game
# 3. Settings
# 4. Quit37.2.4) enumerate() со строками и другими итерируемыми объектами
Функция enumerate() работает с любым итерируемым объектом, не только со списками:
# Перечисление символов в строке
word = "Python"
for position, letter in enumerate(word):
print(f"Letter {position}: {letter}")
# Output:
# Letter 0: P
# Letter 1: y
# Letter 2: t
# Letter 3: h
# Letter 4: o
# Letter 5: n
# Перечисление кортежа
coordinates = (10, 20, 30, 40)
for index, value in enumerate(coordinates):
print(f"Coordinate {index}: {value}")
# Output:
# Coordinate 0: 10
# Coordinate 1: 20
# Coordinate 2: 30
# Coordinate 3: 40Функция enumerate() делает код более читаемым и менее подверженным ошибкам. Каждый раз, когда в цикле нужны и позиция, и значение, используйте enumerate(), а не управляйте счётчиком вручную.
37.3) Объединение последовательностей с помощью zip()
Функция zip() объединяет несколько итерируемых объектов поэлементно, создавая пары (или кортежи) соответствующих элементов. Это бесценно, когда нужно одновременно обрабатывать связанные данные из отдельных последовательностей.
37.3.1) Понимание того, как работает zip()
Функция zip() принимает два или больше итерируемых объектов и возвращает итератор кортежей, где каждый кортеж содержит по одному элементу из каждого входного итерируемого объекта:
# Объединение двух списков
names = ["Alice", "Bob", "Charlie"]
ages = [25, 30, 35]
combined = list(zip(names, ages))
print(combined)
# Output: [('Alice', 25), ('Bob', 30), ('Charlie', 35)]Название «zip» происходит от застёжки-молнии на одежде — она соединяет две отдельные стороны в одну общую структуру, элемент за элементом.
Вот визуальное представление того, как zip() сопоставляет элементы:
37.3.2) Использование zip() в циклах
Самое распространённое применение zip() — в циклах for, когда нужно перебирать несколько последовательностей одновременно:
# Обработка параллельных данных
students = ["Alice", "Bob", "Charlie", "Diana"]
scores = [92, 85, 88, 95]
for student, score in zip(students, scores):
print(f"{student} scored {score}")
# Output:
# Alice scored 92
# Bob scored 85
# Charlie scored 88
# Diana scored 95Это намного чище, чем использовать индексы:
# Без zip() — сложнее и более подвержено ошибкам
students = ["Alice", "Bob", "Charlie", "Diana"]
scores = [92, 85, 88, 95]
for i in range(len(students)):
print(f"{students[i]} scored {scores[i]}")
# Same output, but more code and potential for index errors37.3.3) Обработка последовательностей разной длины
Когда входные последовательности имеют разную длину, zip() останавливается, когда заканчивается самая короткая последовательность:
# Последовательности неравной длины
names = ["Alice", "Bob", "Charlie", "Diana"]
ages = [25, 30] # Only 2 ages
for name, age in zip(names, ages):
print(f"{name} is {age} years old")
# Output:
# Alice is 25 years old
# Bob is 30 years old
# (Charlie and Diana are not processed)Такое поведение предотвращает ошибки, но может привести к незаметной потере данных, если вы не внимательны. Всегда проверяйте, что ваши последовательности имеют ожидаемую длину:
# Проверка несоответствия длины
names = ["Alice", "Bob", "Charlie"]
ages = [25, 30]
if len(names) != len(ages):
print(f"Warning: {len(names)} names but {len(ages)} ages")
print("Only processing the first", min(len(names), len(ages)), "entries")
# Output: Warning: 3 names but 2 ages
# Output: Only processing the first 2 entries
# Продолжаем с zip() — он остановится на самой короткой
for name, age in zip(names, ages):
print(f"{name} is {age} years old")37.3.4) Объединение более чем двух последовательностей с помощью zip()
Функция zip() может объединять любое количество итерируемых объектов:
# Объединение трёх последовательностей
products = ["Laptop", "Mouse", "Keyboard"]
prices = [999.99, 24.99, 79.99]
quantities = [5, 20, 15]
print("Inventory Report:")
for product, price, quantity in zip(products, prices, quantities):
total_value = price * quantity
print(f"{product}: ${price} × {quantity} = ${total_value:.2f}")
# Output:
# Inventory Report:
# Laptop: $999.99 × 5 = $4999.95
# Mouse: $24.99 × 20 = $499.80
# Keyboard: $79.99 × 15 = $1199.8537.3.5) Создание словарей с помощью zip()
Мощный приём — использовать zip() для создания словарей из отдельных последовательностей ключей и значений:
# Создание словаря из двух списков
keys = ["name", "age", "city"]
values = ["Alice", 25, "Boston"]
person = dict(zip(keys, values))
print(person)
# Output: {'name': 'Alice', 'age': 25, 'city': 'Boston'}37.4) Булева агрегация с any() и all()
Функции any() и all() проверяют условия по всему итерируемому объекту, возвращая один булев результат. Это мощные инструменты для валидации и принятия решений на основе множества условий.
37.4.1) Понимание any(): True, если хотя бы один элемент True
Функция any() возвращает True, если хотя бы один элемент в итерируемом объекте является истинным (truthy) (оценивается как True). Если все элементы ложные (falsy), она возвращает False:
# Базовые примеры any()
print(any([True, False, False])) # Output: True (at least one True)
print(any([False, False, False])) # Output: False (all False)
print(any([False, True, True])) # Output: True (multiple True values)
# Пустые итерируемые объекты
print(any([])) # Output: False (no elements to be True)Функция any() использует правила истинности (truthiness) Python (как мы изучали в главе 7). Ненулевые числа, непустые строки и непустые коллекции считаются истинными:
# any() с различными truthy/falsy значениями
print(any([0, 0, 1])) # Output: True (1 is truthy)
print(any([0, 0, 0])) # Output: False (all zeros are falsy)
print(any(["", "", "text"])) # Output: True ("text" is truthy)
print(any(["", "", ""])) # Output: False (empty strings are falsy)37.4.2) Практическое применение any()
Пример: Проверка, выполняется ли хоть одно условие
# Проверка, есть ли неудовлетворительная оценка (ниже 60)
scores = [75, 82, 55, 90, 88]
has_failing_grade = any(score < 60 for score in scores)
if has_failing_grade:
print("Warning: At least one failing grade")
# Output: Warning: At least one failing grade
else:
print("All grades are passing")37.4.3) Понимание all(): True только если все элементы True
Функция all() возвращает True только если все элементы в итерируемом объекте являются истинными (truthy). Если хотя бы один элемент ложный (falsy), она возвращает False:
# Базовые примеры all()
print(all([True, True, True])) # Output: True (all True)
print(all([True, False, True])) # Output: False (one False)
print(all([True, True, False])) # Output: False (one False)
# Пустые итерируемые объекты
print(all([])) # Output: True (vacuous truth - no False elements)Поведение с пустыми итерируемыми объектами может показаться удивительным: all([]) возвращает True. Это называется пустая истина(vacuous truth) — утверждение «все элементы True» технически истинно, когда нет элементов, которые могли бы ему противоречить.
# all() с различными truthy/falsy значениями
print(all([1, 2, 3])) # Output: True (all non-zero)
print(all([1, 0, 3])) # Output: False (0 is falsy)
print(all(["a", "b", "c"])) # Output: True (all non-empty)
print(all(["a", "", "c"])) # Output: False (empty string is falsy)37.4.4) Практическое применение all()
Пример: Валидация, что все условия выполняются
# Проверка, что все оценки проходные (60 или выше)
scores = [75, 82, 68, 90, 88]
all_passing = all(score >= 60 for score in scores)
if all_passing:
print("Congratulations! All grades are passing")
# Output: Congratulations! All grades are passing
else:
print("Some grades need improvement")37.4.5) Вычисление с коротким замыканием в any() и all()
И any(), и all() используют вычисление с коротким замыканием(short-circuit evaluation) (как мы изучали в главе 9). Они прекращают проверки, как только результат становится определённым:
# Функция, которая печатает при вызове (чтобы показать выполнение)
def is_positive(n):
print(f"Checking {n}")
return n > 0
# any() останавливается на первом True
print("Testing any():")
numbers = [0, 0, 1, 2, 3]
result = any(is_positive(n) for n in numbers)
# Output:
# Testing any():
# Checking 0
# Checking 0
# Checking 1
# (Stops here - doesn't check 2 or 3)
print(f"Result: {result}") # Output: Result: True
print("\nTesting all():")
numbers = [1, 2, 0, 3, 4]
result = all(is_positive(n) for n in numbers)
# Output:
# Testing all():
# Checking 1
# Checking 2
# Checking 0
# (Stops here - doesn't check 3 or 4)
print(f"Result: {result}") # Output: Result: FalseЭто делает any() и all() эффективными — они не тратят время на проверку элементов после того, как результат уже определён.
37.5) Сортировка с помощью sorted() и пользовательских ключей
Функция sorted() создаёт новый отсортированный список из любого итерируемого объекта. В отличие от метода .sort() (который работает только со списками и изменяет их на месте), sorted() работает с любым итерируемым объектом и всегда возвращает новый список.
37.5.1) Базовая сортировка с помощью sorted()
Функция sorted() по умолчанию упорядочивает элементы по возрастанию:
# Сортировка чисел
numbers = [42, 17, 93, 8, 55]
sorted_numbers = sorted(numbers)
print(sorted_numbers) # Output: [8, 17, 42, 55, 93]
# Исходный список не изменяется
print(numbers) # Output: [42, 17, 93, 8, 55]
# Сортировка строк (по алфавиту)
names = ["Charlie", "Alice", "Bob", "Diana"]
sorted_names = sorted(names)
print(sorted_names) # Output: ['Alice', 'Bob', 'Charlie', 'Diana']Функция sorted() работает с любым итерируемым объектом, не только со списками:
# Сортировка кортежа (возвращает список)
coordinates = (5, 2, 8, 1, 9)
sorted_coords = sorted(coordinates)
print(sorted_coords) # Output: [1, 2, 5, 8, 9]
# Сортировка строки (возвращает список символов)
word = "python"
sorted_letters = sorted(word)
print(sorted_letters) # Output: ['h', 'n', 'o', 'p', 't', 'y']
# Сортировка множества (возвращает отсортированный список)
unique_numbers = {5, 8, 2, 1}
sorted_unique = sorted(unique_numbers)
print(sorted_unique) # Output: [1, 2, 5, 8]37.5.2) Сортировка в обратном порядке
Используйте параметр reverse=True, чтобы сортировать по убыванию:
# По убыванию для чисел
scores = [85, 92, 78, 95, 88]
highest_first = sorted(scores, reverse=True)
print(highest_first) # Output: [95, 92, 88, 85, 78]
# По убыванию для строк (обратный алфавитный порядок)
names = ["Charlie", "Alice", "Bob", "Diana"]
reverse_alpha = sorted(names, reverse=True)
print(reverse_alpha) # Output: ['Diana', 'Charlie', 'Bob', 'Alice']37.5.3) Понимание параметра key
Параметр key — это то, где sorted() становится по-настоящему мощной. Он меняет то, как Python сравнивает элементы при сортировке.
Что такое параметр key?
Параметр key принимает функцию(function). Python вызывает эту функцию для каждого элемента, чтобы извлечь «ключ сравнения», а затем сортирует по этим ключам, а не по исходным элементам.
Как это работает пошагово:
- Python вызывает ключевую функцию для каждого элемента
- Python собирает все ключи
- Python сортирует, сравнивая эти ключи
- Python возвращает исходные элементы в новом порядке
# Пример: сортировка по длине
words = ["python", "is", "awesome"]
# Шаг 1: Python вызывает len() для каждого слова
# len("python") → 6
# len("is") → 2
# len("awesome") → 7
# Шаг 2: Python получает такие ключи: [6, 2, 7]
# Шаг 3: Python сортирует ключи: [2, 6, 7]
# Шаг 4: Python возвращает words в этом порядке: ["is", "python", "awesome"]
result = sorted(words, key=len)
print(result) # Output: ['is', 'python', 'awesome']Визуализация ключевой функции:
Какой может быть key-функция?
Ключевая функция должна:
- Принимать один аргумент (элемент, который сортируется)
- Возвращать значение, которое Python умеет сравнивать (числа, строки, кортежи и т. п.)
# Встроенные функции отлично подходят
sorted(numbers, key=abs) # Сортировать по абсолютному значению
sorted(words, key=len) # Сортировать по длине
sorted(names, key=str.lower) # Сортировать без учёта регистра
# Ваши собственные функции
def first_letter(word):
return word[0]
sorted(words, key=first_letter) # Сортировать по первой букве
# Lambda-функции (глава 23)
sorted(words, key=lambda w: w[-1]) # Сортировать по последней буквеВажно: key-функция вызывается один раз на элемент
# Демонстрация того, когда вызывается key-функция
def show_key(word):
print(f"Getting key for: {word}")
return len(word)
words = ["cat", "elephant", "dog"]
result = sorted(words, key=show_key)
# Output:
# Getting key for: cat
# Getting key for: elephant
# Getting key for: dog
print(result) # Output: ['cat', 'dog', 'elephant']Важно: key-функция вызывается один раз на элемент
Обратите внимание, что show_key вызывается ровно один раз для каждого слова, а не многократно в процессе сравнений. Python работает эффективно — сначала извлекает все ключи, кэширует их, а затем сортирует, используя кэшированные ключи.
Думайте о key как об ответе на вопрос: «Какой аспект нужно сравнивать?»
key=len→ «Сравнивать по длине»key=abs→ «Сравнивать по абсолютному значению»key=str.lower→ «Сравнивать так, как будто всё в нижнем регистре»key=lambda x: x[1]→ «Сравнивать по второму элементу»
Параметр key позволяет сортировать по любому свойству ваших элементов, делая sorted() невероятно универсальной.
37.5.4) Сортировка со встроенными функциями в качестве ключей
Встроенные функции Python отлично подходят в качестве key-функций:
# Сортировка по абсолютному значению
numbers = [-5, 2, -8, 1, -3, 7]
sorted_by_magnitude = sorted(numbers, key=abs)
print(sorted_by_magnitude) # Output: [1, 2, -3, -5, 7, -8]
# Сортировка строк без учёта регистра
names = ["alice", "Bob", "CHARLIE", "diana"]
sorted_case_insensitive = sorted(names, key=str.lower)
print(sorted_case_insensitive) # Output: ['alice', 'Bob', 'CHARLIE', 'diana']37.5.5) Сортировка сложных структур данных
При сортировке списков кортежей или списков можно использовать индексацию, чтобы указать, по какому элементу сортировать:
# Сортировка кортежей по второму элементу
students = [
("Alice", 92),
("Bob", 85),
("Charlie", 88),
("Diana", 95)
]
# Сортировать по баллу (второй элемент)
by_score = sorted(students, key=lambda student: student[1])
print(by_score)
# Output: [('Bob', 85), ('Charlie', 88), ('Alice', 92), ('Diana', 95)]
# Сортировать по баллу по убыванию
by_score_desc = sorted(students, key=lambda student: student[1], reverse=True)
print(by_score_desc)
# Output: [('Diana', 95), ('Alice', 92), ('Charlie', 88), ('Bob', 85)]Примечание: Здесь мы используем lambda (как мы изучали в главе 23). Lambda — это небольшая анонимная функция. Выражение lambda student: student[1] создаёт функцию, которая принимает кортеж student и возвращает его второй элемент (балл).
37.5.6) Многоуровневая сортировка
Можно сортировать по нескольким критериям, возвращая кортеж из key-функции. Python сравнивает кортежи элемент за элементом, слева направо:
Как работает сравнение кортежей:
Когда Python сравнивает два кортежа, он следует таким правилам:
- Сравнить первые элементы. Если они различаются, сравнение завершено.
- Если первые элементы равны, сравнить вторые элементы.
- Если вторые элементы равны, сравнить третьи элементы.
- Продолжать, пока не найдётся различие или не закончатся элементы.
# Примеры сравнения кортежей
print((1, 'a') < (2, 'z')) # Output: True (1 < 2, so True immediately)
print((1, 'z') < (1, 'a')) # Output: False (1 == 1, so compare 'z' < 'a')
print((1, 'a') < (1, 'a')) # Output: False (both tuples are equal)
print((1, 2, 9) < (1, 3, 1)) # Output: True (1 == 1, then 2 < 3)Это делает кортежи идеальными для многоуровневой сортировки — Python автоматически обрабатывает логику «сравнить первый критерий, затем второй, затем третий» за вас:
# Сортировка по нескольким критериям
students = [
("Alice", "Smith", 92),
("Bob", "Jones", 85),
("Alice", "Brown", 88),
("Charlie", "Smith", 85)
]
# Сортировать по имени, затем по фамилии
by_name = sorted(students, key=lambda s: (s[0], s[1]))
print("By name:")
for student in by_name:
print(f" {student}")
# Output:
# By name:
# ('Alice', 'Brown', 88)
# ('Alice', 'Smith', 92)
# ('Bob', 'Jones', 85)
# ('Charlie', 'Smith', 85)
# Сортировать по баллу по убыванию, затем по имени по возрастанию
by_score_then_name = sorted(students, key=lambda s: (-s[2], s[0]))
print("\nBy score (high to low), then name:")
for student in by_score_then_name:
print(f" {student}")
# Output:
# By score (high to low), then name:
# ('Alice', 'Smith', 92)
# ('Alice', 'Brown', 88)
# ('Bob', 'Jones', 85)
# ('Charlie', 'Smith', 85)Примечание: Чтобы сортировать один критерий по убыванию, а другой по возрастанию, мы берём отрицание числового значения (-s[2]). Это работает, потому что отрицание разворачивает порядок сортировки для чисел. В примере выше -s[2] сортирует баллы от большего к меньшему, а s[0] сортирует имена от A до Z.
37.5.7) Использование вспомогательных функций для сложных ключей
Когда логика сортировки становится сложной, определение вспомогательной функции делает код более читаемым и сопровождаемым. Затем вы можете использовать эту вспомогательную функцию внутри key-функции.
Пример: сортировка файлов по расширению
Предположим, вы хотите сгруппировать файлы по расширению (.csv, .jpg, .pdf и т. д.), а внутри каждой группы сортировать по алфавиту по имени файла. Key-функция должна извлечь расширение файла, что требует некоторой обработки строк.
# Сортировать файлы по расширению, затем по имени
files = [
"report.pdf",
"data.csv",
"image.jpg",
"notes.txt",
"backup.csv",
"photo.jpg"
]
# Извлечь расширение для сортировки
def get_extension(filename):
"""Извлечь расширение файла из имени файла."""
return filename.split(".")[-1] # Разделить по "." и взять последнюю часть
# Использовать вспомогательную функцию в key
sorted_files = sorted(files, key=lambda f: (get_extension(f), f))
print("Files sorted by extension, then name:")
for file in sorted_files:
print(f" {file}")
# Output:
# Files sorted by extension, then name:
# backup.csv # csv files first (alphabetically)
# data.csv # csv files first (alphabetically)
# image.jpg # jpg files next
# photo.jpg # jpg files next
# report.pdf # pdf files next
# notes.txt # txt files lastКак это работает:
- Key-функция
lambda f: (get_extension(f), f)возвращает кортеж для каждого имени файла - Для "report.pdf" она возвращает
("pdf", "report.pdf") - Для "data.csv" она возвращает
("csv", "data.csv") - Python сортирует по первому элементу кортежа (расширению), затем по второму элементу (полному имени файла)
- Это группирует файлы по расширению и сортирует по алфавиту внутри каждой группы
Зачем использовать вспомогательную функцию?
Сравните читаемость:
# Без вспомогательной функции — сложнее понять
sorted_files = sorted(files, key=lambda f: (f.split(".")[-1], f))
# Со вспомогательной функцией — намерение яснее
sorted_files = sorted(files, key=lambda f: (get_extension(f), f))Вспомогательная функция делает ваш код самодокументируемым. Любой, кто читает get_extension(f), сразу понимает, что происходит, тогда как f.split(".")[-1] требует мысленного разбора.
37.5.8) sorted() vs .sort(): когда использовать каждую
Python предоставляет два способа сортировки:
sorted()— функция, которая возвращает новый отсортированный список.sort()— метод списка, который сортирует на месте
# sorted() — создаёт новый список, исходный не меняется
numbers = [3, 1, 4, 1, 5]
sorted_numbers = sorted(numbers)
print(f"Original: {numbers}") # Output: Original: [3, 1, 4, 1, 5]
print(f"Sorted: {sorted_numbers}") # Output: Sorted: [1, 1, 3, 4, 5]
# .sort() — изменяет список на месте, возвращает None
numbers = [3, 1, 4, 1, 5]
result = numbers.sort()
print(f"Modified: {numbers}") # Output: Modified: [1, 1, 3, 4, 5]
print(f"Return value: {result}") # Output: Return value: NoneКогда использовать sorted():
- Нужно сохранить исходный порядок
- Вы сортируете что-то кроме списка (кортеж, строку, множество и т. д.)
- Вы хотите отсортировать и присвоить в одном выражении
Когда использовать .sort():
- У вас есть список, и исходный порядок не нужен
- Вы хотите экономить память (новый список не создаётся)
- Вы сортируете большой список на месте ради эффективности
Функция sorted() — один из самых универсальных инструментов Python. В сочетании с параметром key она может справиться практически с любыми требованиями к сортировке — от простого упорядочивания чисел до сложной сортировки вложенных структур данных по нескольким критериям.
Эта глава дала вам набор основных встроенных функций и инструментов Python. Вы научились:
- Понимать иерархию типов Python и предсказывать, какие операции работают с какими типами
- Использовать базовые функции вроде
len(),sum(),min(),max(),abs()иround()для типовых операций - Перебирать с информацией о позиции с помощью
enumerate() - Обрабатывать параллельные последовательности одновременно с помощью
zip() - Принимать решения по коллекциям, используя
any()иall() - Гибко сортировать данные с помощью
sorted()и пользовательских key-функций
Эти инструменты составляют основу идиоматичного кода Python. Они эффективны, читаемы и корректно обрабатывают пограничные случаи. По мере того как вы продолжаете программировать, вы будете постоянно обращаться к этим функциям — это строительные блоки, которые делают код на Python элегантным и выразительным.
В следующей главе мы изучим декораторы(decorators), которые позволяют изменять и расширять поведение функций(function) мощными способами, опираясь на концепции функций как объектов первого класса, которые мы изучали в главе 23.