Beaucoup de réponses contiennent de petites informations complètes, je voudrais donc les rassembler avec mes motifs préférés.
la valeur par défaut est un mutable
type
Si la valeur par défaut est un objet mutable, vous avez de la chance: vous pouvez exploiter le fait que les arguments par défaut de Python sont évalués une fois lorsque la fonction est définie (plus d'informations à ce sujet à la fin de la réponse dans la dernière section)
Cela signifie que vous pouvez facilement comparer une valeur mutable par défaut en utilisant is
pour voir si elle a été passée en argument ou laissée par défaut, comme dans les exemples suivants en tant que fonction ou méthode:
def f(value={}):
if value is f.__defaults__[0]:
print('default')
else:
print('passed in the call')
et
class A:
def f(self, value={}):
if value is self.f.__defaults__[0]:
print('default')
else:
print('passed in the call')
Arguments par défaut immuables
Maintenant, c'est un peu moins élégant si on s'attend à ce que votre valeur par défaut soit une immutable
valeur (et rappelez-vous que même les chaînes sont immuables!) Parce que vous ne pouvez pas exploiter l'astuce telle quelle, mais il y a toujours quelque chose que vous pouvez faire, en exploitant toujours mutable type; en gros, vous mettez un "faux" par défaut mutable dans la signature de la fonction, et la valeur par défaut "réelle" souhaitée dans le corps de la fonction.
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)
Cela semble particulièrement amusant si votre valeur par défaut est None
, mais qu'elle None
est immuable, donc ... vous devez toujours utiliser explicitement un mutable comme paramètre par défaut de la fonction et basculer sur None dans le code.
Utilisation d'une Default
classe pour les valeurs par défaut immuables
ou, similaire à la suggestion @cz, si les documents python ne suffisent pas :-), vous pouvez ajouter un objet entre les deux pour rendre l'API plus explicite (sans lire les documents); l'instance de classe used_proxy_ Default est mutable et contiendra la valeur par défaut réelle que vous souhaitez utiliser.
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))
maintenant:
>>> f()
default
Default Value: 1 (<class 'int'>)
argument is: 1
>>> f(2)
passed in the call
argument is: 2
Ce qui précède fonctionne bien aussi pour Default(None)
.
Autres modèles
De toute évidence, les modèles ci-dessus semblent plus laids qu'ils ne le devraient à cause de tous ceux print
qui ne sont là que pour montrer comment ils fonctionnent. Sinon, je les trouve assez laconiques et répétables.
Vous pouvez écrire un décorateur pour ajouter le __call__
modèle suggéré par @dmg de manière plus simple, mais cela obligera toujours à utiliser des astuces étranges dans la définition de la fonction elle-même - vous devrez vous séparer value
et value_default
si votre code a besoin de les distinguer, alors Je ne vois pas beaucoup d'avantage et je n'écrirai pas l'exemple :-)
Types mutables comme valeurs par défaut dans Python
Un peu plus sur # 1 python gotcha! , abusé pour votre propre plaisir ci-dessus. Vous pouvez voir ce qui se passe en raison de l' évaluation à la définition en faisant:
def testme(default=[]):
print(id(default))
Vous pouvez exécuter testme()
autant de fois que vous le souhaitez, vous verrez toujours une référence à la même instance par défaut (donc fondamentalement votre valeur par défaut est immuable :-)).
Rappelez - vous qu'en Python il y a seulement 3 mutable types intégrés : set
, list
, dict
; tout le reste - même les cordes! - est immuable.