Настройка дефолтных значений аргументов
Параметры функций со значениями по умолчанию
Значение по умолчанию для аргумента функции задается в определении после знака равенства. Этот подход работает для неизменяемых типов (числа, строки, кортежи, None) и позволяет вызывать функцию без указания соответствующего аргумента.
def greet(name='Guest'):
print(f'Hello, {name}')Python значение по умолчанию (значение по умолчанию в python)
greet() # Hello, Guest
greet('Alice') # Hello, Alice
В данном примере параметр name получает строку 'Guest' при отсутствии аргумента. Такое значение вычисляется один раз в момент определения функции и сохраняется в атрибуте __defaults__.
Как обработать изменяемый объект в качестве значения по умолчанию?
Если значением по умолчанию выступает изменяемый объект (список, словарь, множество), он создаётся один раз и разделяется между всеми вызовами функции. Это приводит к накоплению данных между вызовами, что часто становится неожиданным поведением.
def add_item(item, lst=[]):
lst.append(item)
return lst
print(add_item(1)) # [1] print(add_item(2)) # [1, 2] (ожидалось [2])
Такая ошибка возникает из-за того, что список lst создаётся при определении функции и используется во всех вызовах.
Рекомендуемый приём - использовать None как sentinel и создавать новый изменяемый объект внутри функции.
def add_item(item, lst=None):
if lst is None:
lst = []
lst.append(item)
return lst
print(add_item(1)) # [1] print(add_item(2)) # [2]
Теперь каждый вызов без аргумента lst создаёт новый список. Аналогом может быть использование фабричной функции, например lambda, но она недопустима непосредственно в сигнатуре - применяется в декораторах или dataclasses.field.
Как отличить отсутствие аргумента от явной передачи None?
Иногда требуется различать две ситуации: аргумент не передан, либо передан со значением None. Для этого применяют специальный объект-синглтон:
_MISSING = object()
def process(data=_MISSING):
if data is _MISSING:
print('Аргумент не передан')
else:
print(f'Получено: {data}')
process() # Аргумент не передан process(None) # Получено: None process(42) # Получено: 42
Этот подход позволяет обрабатывать None как корректное значение, не путая его с пропущенным аргументом.
Как задать значение по умолчанию, вычисляемое при каждом вызове?
Иногда значение по умолчанию должно зависеть от времени или контекста вызова. Непосредственно в сигнатуре это невозможно, поэтому используют фабричную функцию, вызываемую внутри тела:
import datetime
def log_event(message, timestamp=None):
if timestamp is None:
timestamp = datetime.datetime.now()
print(f'[{timestamp}] {message}')
log_event('start') # [2025-03-19 12:34:56] start
Для более элегантного синтаксиса (например, в классах данных) применяется dataclasses.field с default_factory.
from dataclasses import dataclass, field
import datetime
@dataclass
class Event:
message: str
timestamp: datetime.datetime = field(default_factory=datetime.datetime.now)
При создании экземпляра без timestamp вызывается datetime.now() для каждого объекта отдельно.
Как зафиксировать часть аргументов с помощью functools.partial?
Библиотека functools предоставляет partial для создания новой функции с предустановленными значениями некоторых аргументов.
from functools import partial
def power(base, exponent):
return base ** exponent
square = partial(power, exponent=2)
cube = partial(power, exponent=3)
print(square(5)) # 25 print(cube(2)) # 8
Этот приём удобен, когда одна функция часто вызывается с одинаковыми значениями отдельных параметров.
Как использовать значения по умолчанию в декораторах?
Декораторы могут добавлять аргументы со значениями по умолчанию к оборачиваемой функции. Например, таймер с опциональным единицами измерения:
import time
from functools import wraps
def timer(unit='seconds'):
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
start = time.time()
result = func(*args, **kwargs)
elapsed = time.time() - start
if unit == 'milliseconds':
elapsed *= 1000
print(f'{func.__name__} заняла {elapsed:.4f} {unit}')
return result
return wrapper
return decorator
Параметр unit имеет значение по умолчанию 'seconds', которое можно переопределить при применении декоратора.
Расширенные примеры со значениями по умолчанию
Вложенные изменяемые объекты и deepcopy
Если значение по умолчанию - сложная структура (список списков), копирование через None может дать неверный результат, если требуется полная независимость. Используйте copy.deepcopy внутри тела.
from copy import deepcopy
def add_matrix(row, matrix=None):
if matrix is None:
matrix = [[0,0],[0,0]]
else:
matrix = deepcopy(matrix)
matrix.append(row)
return matrix
m1 = add_matrix([1,2]) m2 = add_matrix([3,4]) print(m1) # [[0,0],[0,0],[1,2]] print(m2) # [[0,0],[0,0],[3,4]]
Без deepcopy второй вызов модифицировал бы общие внутренние списки.
Значения по умолчанию и аннотации типов
Совместное использование аннотаций и значений по умолчанию улучшает читаемость и помогает статическим анализаторам.
def fetch_data(url: str, timeout: int = 30, headers: dict | None = None) -> dict:
if headers is None:
headers = {}
# ... логика запроса
Здесь dict | None (Python 3.10+) указывает, что headers может быть словарём или None, а значение по умолчанию None обрабатывается внутри.
Использование inspect.signature для анализа значений по умолчанию
Модуль inspect позволяет программно получать значения по умолчанию и их параметры.
import inspect
def example(a, b=10, c=None):
pass
sig = inspect.signature(example)
for name, param in sig.parameters.items():
if param.default is not inspect.Parameter.empty:
print(f'{name}: default = {param.default!r}')
b: default = 10 c: default = None
Это полезно для метапрограммирования, создания документации или валидации.
Задание значений по умолчанию через __kwdefaults__ для функций с **kwargs
Если функция использует **kwargs, значения по умолчанию для отдельных ключей можно задать через словарь внутри функции.
def render_template(path, **kwargs):
defaults = {'lang': 'ru', 'cache': True}
config = {**defaults, **kwargs}
# ... использование config
render_template('index.html')
render_template('page.html', lang='en', cache=False)
Такой способ гибок, но не даёт подсказок IDE о доступных параметрах.
Классы с настраиваемыми значениями по умолчанию через __init_subclass__
При наследовании можно изменить значения по умолчанию для методов базового класса.
class Base:
def process(self, limit=100):
print(f'Processing with limit {limit}')
class Child(Base):
def process(self, limit=500):
super().process(limit=limit)
c = Child() c.process() # Processing with limit 500
Здесь дочерний класс переопределяет значение по умолчанию для аргумента limit.
Значения по умолчанию в лямбда-выражениях
Лямбда-функции также могут иметь параметры с умолчаниями.
add = lambda x, delta=1: x + delta
print(add(5)) # 6
print(add(5, 3)) # 8
Однако внутри лямбды нельзя использовать изменяемые умолчания с сохранением состояния - они разделяются, как и в обычных функциях.