Багато відповідей містять невеликі фрагменти повної інформації, тому я хотів би зібрати все разом із улюбленими шаблонами.
значення за замовчуванням - це 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_ за замовчуванням змінюється і міститиме реальне значення за замовчуванням, яке ви хочете використовувати.
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 python gotcha! , зловживання для вашого власного задоволення вище. Ви можете побачити, що відбувається завдяки оцінці при визначенні , виконавши:
def testme(default=[]):
print(id(default))
Ви можете запускати testme()
скільки завгодно часу, ви завжди побачите посилання на той самий екземпляр за замовчуванням (тому в основному ваше значення за замовчуванням незмінне :-)).
Пам'ятайте , що в Python є тільки 3 змінні вбудованих типів : set
, list
, dict
; все інше - навіть струни! - незмінна.