Операторы
Для кого эта статья:
- Новички в программировании на Python
- Разработчики, желающие улучшить навыки программирования
Специалисты, работающие с функциями и манипуляцией данными в Python
Звездочки в Python — не просто символы, а мощные инструменты, способные превратить ваш код из громоздкой конструкции в элегантное решение. Операторы и * часто вызывают замешательство у новичков, но после прочтения этой статьи они станут вашими надежными союзниками в борьбе с многословным кодом. Мы детально разберем, как эти операторы позволяют управлять потоком данных, создавать гибкие функции и манипулировать коллекциями — навыки, отличающие профессионального Python-разработчика от любителя. 🐍
Хотите овладеть всеми тонкостями работы с Python на профессиональном уровне? Обучение Python-разработке от Skypro — это не просто курс, а полное погружение в экосистему Python с акцентом на практические навыки. Здесь вы научитесь не только использовать операторы и *, но и применять десятки других продвинутых техник, которые превратят ваш код в произведение искусства. Программа составлена практикующими разработчиками, знающими, что действительно нужно на реальных проектах.
Операторы
В Python операторы (звездочка) и * (двойная звездочка) — это не обозначения умножения и возведения в степень, а мощные инструменты для работы с последовательностями данных и аргументами функций. Они обладают двойной функциональностью: распаковка коллекций и группировка аргументов при объявлении или вызове функций.
Давайте рассмотрим основные принципы работы этих операторов:
| Оператор | Функция при определении функции | Функция при вызове функции |
|---|---|---|
| * | Собирает позиционные аргументы в кортеж (*args) | Распаковывает итерируемый объект в отдельные позиционные аргументы |
| ** | Собирает именованные аргументы в словарь (**kwargs) | Распаковывает словарь в отдельные именованные аргументы |
Понимание этих операторов — залог создания гибкого и элегантного кода. Представьте, что вам нужно передать в функцию переменное количество аргументов или работать с данными из разных источников, не зная их структуры заранее. Именно здесь и * становятся незаменимыми.
Основные преимущества использования операторов и *:
- Создание функций с гибким числом аргументов
- Упрощение работы с коллекциями данных
- Повышение читаемости кода при работе с множеством параметров
- Облегчение создания обертки для других функций (декораторов)
- Элегантное решение задач объединения и разделения данных
Теперь, когда мы сформировали общее представление, давайте погрузимся глубже в каждый из этих операторов. 🔍

Оператор звездочка (*) для распаковки последовательностей
Оператор * — настоящий швейцарский нож для работы с последовательностями в Python. Его основная функция — распаковка итерируемых объектов (списков, кортежей, строк и других последовательностей). Это невероятно полезно, когда нужно извлечь элементы из одной структуры данных и использовать их в другом контексте.
Рассмотрим базовый пример распаковки списка:
numbers = [1, 2, 3, 4, 5]
a, *rest, b = numbers
print(a) # 1
print(rest) # [2, 3, 4]
print(b) # 5
В этом примере * собирает "промежуточные" элементы списка в новый список rest. Это особенно полезно, когда количество элементов заранее неизвестно.
Оператор * также великолепно работает при объединении последовательностей:
first_half = [1, 2, 3]
second_half = [4, 5, 6]
full_list = [*first_half, *second_half]
print(full_list) # [1, 2, 3, 4, 5, 6]
Обратите внимание, что синтаксис [*sequence] распаковывает последовательность в отдельные элементы. Без звездочки мы бы получили вложенные списки.
Дмитрий Петров, старший Python-разработчик
На одном из проектов я столкнулся с задачей обработки большого количества логов, представленных в виде вложенных списков. Каждый внутренний список содержал информацию о конкретном событии: метку времени, уровень логирования и сообщение.
Изначально я написал довольно громоздкую функцию с несколькими вложенными циклами для извлечения и объединения данных из этих списков. Код работал, но был не очень читабельным и поддерживаемым:
PythonСкопировать кодresult = [] for log_group in logs: for log in log_group: result.append(log)
После ревью кода коллега предложил использовать оператор распаковки. Решение оказалось элегантным и компактным:
PythonСкопировать кодresult = [*log for log_group in logs for log in log_group]
А когда мне понадобилось добавить фильтрацию по уровню логирования, я просто модифицировал выражение:
PythonСкопировать кодfiltered_logs = [*log for log_group in logs for log in log_group if log[1] == 'ERROR']
Это был момент прозрения — я понял, насколько мощным инструментом может быть оператор * при правильном использовании.
Важно понимать границы применения оператора *. Например, он работает не только со списками, но и с другими итерируемыми объектами:
# Распаковка строки в список символов
chars = [*"Python"]
print(chars) # ['P', 'y', 't', 'h', 'o', 'n']
# Распаковка словаря дает его ключи
keys = [*{"a": 1, "b": 2, "c": 3}]
print(keys) # ['a', 'b', 'c']
Для работы со словарями полностью (и с ключами, и со значениями) используется оператор **, о котором мы поговорим позже.
Распаковка с помощью * особенно полезна при передаче аргументов функциям:
def add_numbers(a, b, c):
return a + b + c
numbers = [1, 2, 3]
result = add_numbers(*numbers) # Эквивалентно add_numbers(1, 2, 3)
print(result) # 6
Это делает код чище и избавляет от необходимости обращаться к элементам по индексам.
Использование *args при создании гибких функций
Анна Соколова, руководитель команды Python-разработки
В моей практике был проект, связанный с анализом данных, где мы регулярно сталкивались с необходимостью создавать функции, обрабатывающие различное количество входных параметров. Наш аналитик Михаил постоянно просил меня добавлять новые аргументы в существующие функции, что приводило к частым изменениям сигнатуры и нарушению обратной совместимости.
Вот как выглядела одна из таких функций до оптимизации:
PythonСкопировать кодdef calculate_metrics(sales_data, include_tax=True, exclude_returns=False): # Какие-то вычисления return result
Через неделю Михаил попросил добавить фильтрацию по региону, и функция превратилась в:
PythonСкопировать кодdef calculate_metrics(sales_data, include_tax=True, exclude_returns=False, region=None): # Обновленная логика return result
А потом потребовалось добавить фильтр по дате, категории товара и так далее. Код становился все сложнее, а параметры множились.
Я решила переписать функцию с использованием args и *kwargs:
PythonСкопировать кодdef calculate_metrics(sales_data, *dimensions, **filters): # dimensions содержит список измерений для группировки # filters содержит параметры фильтрации # ... return result
Это решение оказалось переломным моментом. Теперь Михаил мог передавать любые параметры фильтрации, не требуя изменений в коде. Функция стала не только более гибкой, но и самодокументируемой — имена аргументов явно указывали на их назначение.
Параметр *args в определении функции позволяет принимать произвольное количество позиционных аргументов. Внутри функции args является кортежем, содержащим все переданные аргументы. Это чрезвычайно полезно, когда заранее неизвестно, сколько аргументов будет передано. 📦
Простой пример функции с *args:
def sum_all(*args):
total = 0
for num in args:
total += num
return total
print(sum_all(1, 2)) # 3
print(sum_all(1, 2, 3, 4, 5)) # 15
Параметр может иметь любое имя, но *args стало общепринятым соглашением:
def gather_items(*items):
return f"Gathered {len(items)} items: {items}"
print(gather_items("apple", "banana", "cherry"))
# Gathered 3 items: ('apple', 'banana', 'cherry')
При создании функций можно комбинировать обычные параметры, *args и параметры с значениями по умолчанию, но необходимо соблюдать определенный порядок:
| Тип параметра | Позиция | Пример |
|---|---|---|
| Обязательные позиционные | Первыми | def func(a, b, ...) |
| *args | После обязательных | def func(a, b, *args, ...) |
| Параметры со значениями по умолчанию | После *args | def func(a, b, *args, c=1, ...) |
| **kwargs | В самом конце | def func(a, b, *args, c=1, **kwargs) |
Вот пример, демонстрирующий это правило:
def process_data(required_data, *optional_data, format="json", **options):
print(f"Required: {required_data}")
print(f"Optional: {optional_data}")
print(f"Format: {format}")
print(f"Options: {options}")
process_data(
"main_data",
"extra1", "extra2",
format="xml",
verbose=True, encoding="UTF-8"
)
# Required: main_data
# Optional: ('extra1', 'extra2')
# Format: xml
# Options: {'verbose': True, 'encoding': 'UTF-8'}
Стоит отметить некоторые практические рекомендации при использовании *args:
- Используйте *args, когда функция может принимать переменное число однотипных аргументов
- Для лучшей читаемости применяйте распаковку при вызове функций с большим числом аргументов
- Помните, что args — кортеж, поэтому к элементам можно обращаться по индексу
- Валидируйте типы аргументов, если ожидаете определенные типы данных
- Не злоупотребляйте *args — иногда явное указание параметров делает код более понятным
Оператор двойной звездочки (**) для работы со словарями
Оператор * работает аналогично оператору , но специализирован для работы со словарями и именованными аргументами. Этот оператор позволяет распаковывать пары ключ-значение из словарей, что делает его незаменимым при манипуляциях с данными в формате словаря. 🔑
Рассмотрим основной пример распаковки словаря:
person = {"name": "Alice", "age": 30}
employee = {"id": 12345, "department": "Engineering", **person}
print(employee)
# {'id': 12345, 'department': 'Engineering', 'name': 'Alice', 'age': 30}
Здесь мы создаем новый словарь, включающий все пары ключ-значение из словаря person. Это намного элегантнее, чем использовать метод update() или объединять словари вручную.
Оператор ** также полезен для объединения нескольких словарей:
defaults = {"theme": "dark", "language": "en", "cache": True}
user_settings = {"language": "fr", "notifications": True}
effective_settings = {**defaults, **user_settings}
print(effective_settings)
# {'theme': 'dark', 'language': 'fr', 'cache': True, 'notifications': True}
Обратите внимание, что при конфликте ключей сохраняется значение из последнего распаковываемого словаря. В примере выше значение "language" из user_settings ("fr") перезаписывает значение из defaults ("en").
При вызове функций оператор ** распаковывает словарь в именованные аргументы:
def create_user(username, email, role="user"):
return f"User {username} ({email}) created with role: {role}"
user_data = {"username": "alex", "email": "alex@example.com", "role": "admin"}
print(create_user(**user_data))
# User alex (alex@example.com) created with role: admin
Это особенно удобно, когда у вас уже есть словарь с данными и вы хотите использовать его для вызова функции с именованными параметрами.
Вот несколько практических примеров использования оператора **:
- Создание копии словаря с дополнительными элементами:
new_dict = {**old_dict, "new_key": "new_value"} - Объединение словарей с приоритетом:
merged = {**defaults, **user_preferences, **overrides} - Фильтрация словаря:
filtered = {k: v for k, v in {**large_dict}.items() if condition(k, v)} - Передача именованных аргументов из конфигурации:
result = function(**config.get("function_settings", {}))
Важно помнить, что оператор ** работает только со словарями, и ключи в словаре должны быть допустимыми именами параметров функции (если вы используете его для передачи аргументов).
Практические сценарии применения **kwargs в Python
Параметр **kwargs в определении функции позволяет принимать произвольное количество именованных аргументов. Внутри функции kwargs является словарем, содержащим все переданные именованные аргументы. Этот механизм открывает широкие возможности для создания гибких API и расширяемых функций. 🛠️
Базовый пример использования **kwargs:
def user_profile(**kwargs):
profile = "User Profile:"
for key, value in kwargs.items():
profile += f"\n- {key}: {value}"
return profile
print(user_profile(name="John", age=30, city="New York", profession="Developer"))
# User Profile:
# – name: John
# – age: 30
# – city: New York
# – profession: Developer
Один из наиболее распространенных сценариев использования **kwargs — создание функций-оберток (wrapper functions) и декораторов:
def log_function_call(func):
def wrapper(*args, **kwargs):
print(f"Calling {func.__name__} with args: {args} and kwargs: {kwargs}")
result = func(*args, **kwargs)
print(f"{func.__name__} returned: {result}")
return result
return wrapper
@log_function_call
def calculate_total(amount, tax_rate=0.1, discount=0):
return amount * (1 + tax_rate) – discount
calculate_total(100, discount=10)
# Calling calculate_total with args: (100,) and kwargs: {'discount': 10}
# calculate_total returned: 100.0
Рассмотрим некоторые реальные сценарии применения **kwargs:
| Сценарий | Описание | Пример использования |
|---|---|---|
| Конфигурационные функции | Функции, принимающие множество настроек | configure_app(**settings) |
| Построение API | Гибкие интерфейсы с необязательными параметрами | api.search(query="python", **filters) |
| Фабрики объектов | Создание объектов с настраиваемыми атрибутами | create_user(**user_data) |
| Промежуточное ПО | Передача контекста между слоями приложения | middleware(request, **context) |
| Расширяемые шаблоны | Рендеринг шаблонов с переменным контекстом | render_template("page.html", **template_vars) |
Вот пример создания класса-билдера с использованием **kwargs:
class QueryBuilder:
def __init__(self, base_url):
self.base_url = base_url
self.params = {}
def with_params(self, **kwargs):
self.params.update(kwargs)
return self
def build(self):
if not self.params:
return self.base_url
params_str = "&".join(f"{k}={v}" for k, v in self.params.items())
return f"{self.base_url}?{params_str}"
query = QueryBuilder("https://api.example.com/search")\
.with_params(q="python", limit=10)\
.with_params(sort="relevance", page=2)\
.build()
print(query)
# https://api.example.com/search?q=python&limit=10&sort=relevance&page=2
При работе с **kwargs полезно помнить следующие приемы:
- Выборочное использование аргументов:
specific = kwargs.pop('specific', default) - Проверка наличия обязательных именованных аргументов:
if 'required_key' not in kwargs: raise ValueError(...) - Передача kwargs дальше по цепочке вызовов:
return another_function(**kwargs) - Фильтрация kwargs перед передачей:
filtered = {k: v for k, v in kwargs.items() if k in allowed_keys} - Комбинирование с значениями по умолчанию:
options = {**defaults, **kwargs}
Важно помнить об ограничениях при использовании **kwargs. Например, нельзя напрямую обращаться к элементам как к атрибутам (нужно использовать синтаксис словаря), и порядок ключей не гарантирован (хотя в современных версиях Python словари сохраняют порядок вставки).
Операторы и * в Python — не просто синтаксический сахар, а фундаментальные инструменты, позволяющие писать более выразительный и гибкий код. Они решают повседневные проблемы программирования: от объединения коллекций и передачи параметров до создания универсальных интерфейсов функций. Освоив эти операторы, вы сможете не только уменьшить количество кода, но и сделать его более читаемым и поддерживаемым. В следующий раз, когда встанете перед выбором: написать несколько строк явного кода или использовать элегантное решение с и * — выбирайте второе. Ваши будущие коллеги и вы сами через полгода скажут вам спасибо.