Многие ответы содержат небольшие фрагменты полной информации, поэтому я хотел бы собрать все это вместе с моими любимыми шаблонами.
значение по умолчанию - это mutable
тип
Если значением по умолчанию является изменяемый объект, вам повезло: вы можете воспользоваться тем фактом, что аргументы Python по умолчанию оцениваются один раз при определении функции (подробнее об этом в конце ответа в последнем разделе)
Это означает, что вы можете легко сравнить изменяемое значение по умолчанию, используя, is
чтобы увидеть, было ли оно передано в качестве аргумента или оставлено по умолчанию, как в следующих примерах как функция или метод:
def f(value={}):
if value is f.__defaults__[0]:
print('default')
else:
print('passed in the call')
и
class A:
def f(self, value={}):
if value is self.f.__defaults__[0]:
print('default')
else:
print('passed in the call')
Неизменяемые аргументы по умолчанию
Теперь это немного менее элегантно, если ожидается, что ваше значение по умолчанию будет immutable
значением (и помните, что даже строки неизменяемы!), Потому что вы не можете использовать трюк как есть, но вы все равно можете что-то сделать, все еще используя изменяемые тип; в основном вы помещаете изменяемое «фальшивое» значение по умолчанию в сигнатуру функции и желаемое «настоящее» значение по умолчанию в теле функции.
def f(value={}):
"""
my function
:param value: value for my function; default is 1
"""
if value is f.__defaults__[0]:
print('default')
value = 1
else:
print('passed in the call')
print(value)
Это особенно забавно, если у вас настоящее значение по умолчанию None
, но None
оно неизменяемо, поэтому ... вам все равно нужно явно использовать изменяемый параметр в качестве параметра функции по умолчанию и переключиться на None в коде.
Использование Default
класса для неизменяемых значений по умолчанию
или, аналогично предложению @cz, если документов python недостаточно :-), вы можете добавить объект между ними, чтобы сделать API более явным (без чтения документов); Экземпляр класса used_proxy_ Default является изменяемым и будет содержать реальное значение по умолчанию, которое вы хотите использовать.
class Default:
def __repr__(self):
return "Default Value: {} ({})".format(self.value, type(self.value))
def __init__(self, value):
self.value = value
def f(default=Default(1)):
if default is f.__defaults__[0]:
print('default')
print(default)
default = default.value
else:
print('passed in the call')
print("argument is: {}".format(default))
в настоящее время:
>>> f()
default
Default Value: 1 (<class 'int'>)
argument is: 1
>>> f(2)
passed in the call
argument is: 2
Вышеупомянутое также хорошо работает для Default(None)
.
Другие шаблоны
Очевидно, что приведенные выше шаблоны выглядят уродливее, чем должны, потому что все print
они предназначены только для демонстрации того, как они работают. В остальном я считаю их достаточно лаконичными и повторяемыми.
Вы можете написать декоратор, чтобы добавить __call__
шаблон, предложенный @dmg, более упрощенным способом, но это все равно обяжет использовать странные уловки в самом определении функции - вам нужно будет разделить value
иvalue_default
если ваш код должен различать их, поэтому Особого преимущества не вижу и пример писать не буду :-)
Изменяемые типы как значения по умолчанию в Python
Еще немного о питоне # 1! , оскорбляли для собственного удовольствия выше. Вы можете увидеть, что происходит из-за оценки при определении , выполнив:
def testme(default=[]):
print(id(default))
Вы можете запускать testme()
столько раз, сколько хотите, вы всегда будете видеть ссылку на один и тот же экземпляр по умолчанию (так что в основном ваше значение по умолчанию является неизменным :-)).
Помните , что в Python есть только 3 изменяемые встроенных типов : set
, list
, dict
; все остальное - даже струны! - неизменен.