Muitas respostas contêm pequenos pedaços de informações completas, então eu gostaria de juntar tudo com meu (s) padrão (ões) favorito (s).
o valor padrão é um mutable
tipo
Se o valor padrão for um objeto mutável, você tem sorte: você pode explorar o fato de que os argumentos padrão do Python são avaliados uma vez quando a função é definida (mais informações sobre isso no final da resposta na última seção)
Isso significa que você pode comparar facilmente um valor mutável padrão usando is
para ver se ele foi passado como um argumento ou deixado como padrão, como nos exemplos a seguir como função ou método:
def f(value={}):
if value is f.__defaults__[0]:
print('default')
else:
print('passed in the call')
e
class A:
def f(self, value={}):
if value is self.f.__defaults__[0]:
print('default')
else:
print('passed in the call')
Argumentos padrão imutáveis
Agora, é um pouco menos elegante se for esperado que seu padrão seja um immutable
valor (e lembre-se de que até mesmo strings são imutáveis!) Porque você não pode explorar o truque como ele é, mas ainda há algo que você pode fazer, ainda explorando mutável tipo; basicamente, você coloca um padrão "falso" mutável na assinatura da função e o valor padrão "real" desejado no corpo da função.
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)
É particularmente engraçado se o seu padrão real for None
, masNone
é imutável, então ... você ainda precisa usar explicitamente um mutável como o parâmetro padrão da função e alternar para Nenhum no código.
Usando um Default
classe para padrões imutáveis
ou, semelhante à sugestão do @cz, se os documentos do python não forem suficientes :-), você pode adicionar um objeto no meio para tornar a API mais explícita (sem ler os documentos); a instância da classe used_proxy_ Default é mutável e conterá o valor padrão real que você deseja usar.
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))
agora:
>>> f()
default
Default Value: 1 (<class 'int'>)
argument is: 1
>>> f(2)
passed in the call
argument is: 2
O acima também funciona bem para Default(None)
.
Outros padrões
Obviamente, os padrões acima parecem mais feios do que deveriam por causa de todos os print
que existem apenas para mostrar como funcionam. Caso contrário, acho que são concisos e repetíveis o suficiente.
Você poderia escrever um decorador para adicionar o __call__
padrão sugerido por @dmg de uma forma mais simplificada, mas isso ainda obrigará o uso de truques estranhos na própria definição da função - você precisaria separar value
evalue_default
se seu código precisar distingui-los, Não vejo muita vantagem e não vou escrever o exemplo :-)
Tipos mutáveis como valores padrão em Python
Um pouco mais sobre # 1 python gotcha! , abusado para seu próprio prazer acima. Você pode ver o que acontece devido à avaliação na definição , fazendo:
def testme(default=[]):
print(id(default))
Você pode executar testme()
quantas vezes quiser, você sempre verá uma referência à mesma instância padrão (então, basicamente, seu padrão é imutável :-)).
Lembre-se que em Python existem apenas 3 mutável built-in tipos : set
, list
, dict
; tudo o mais - até cordas! - é imutável.