Aproximadamente, partial
faz algo assim (além do suporte à palavra-chave args, etc.):
def partial(func, *part_args):
def wrapper(*extra_args):
args = list(part_args)
args.extend(extra_args)
return func(*args)
return wrapper
Assim, chamando partial(sum2, 4)
você cria uma nova função (uma chamada, para ser preciso) que se comporta como sum2
, mas tem um argumento posicional a menos. Esse argumento ausente é sempre substituído por 4
, para quepartial(sum2, 4)(2) == sum2(4, 2)
Quanto ao motivo, é necessário vários casos. Por um lado, suponha que você precise passar uma função em algum lugar onde se espera que tenha 2 argumentos:
class EventNotifier(object):
def __init__(self):
self._listeners = []
def add_listener(self, callback):
''' callback should accept two positional arguments, event and params '''
self._listeners.append(callback)
# ...
def notify(self, event, *params):
for f in self._listeners:
f(event, params)
Mas uma função que você já tem precisa acessar algum terceiro context
objeto para realizar seu trabalho:
def log_event(context, event, params):
context.log_event("Something happened %s, %s", event, params)
Portanto, existem várias soluções:
Um objeto personalizado:
class Listener(object):
def __init__(self, context):
self._context = context
def __call__(self, event, params):
self._context.log_event("Something happened %s, %s", event, params)
notifier.add_listener(Listener(context))
Lambda:
log_listener = lambda event, params: log_event(context, event, params)
notifier.add_listener(log_listener)
Com parciais:
context = get_context() # whatever
notifier.add_listener(partial(log_event, context))
Desses três, partial
é o mais curto e o mais rápido. (Para um caso mais complexo, você pode querer um objeto personalizado).
extra_args
variávelextra_args
é algo que foi passado pelo chamador parcial, no exemplo comp = partial(func, 1); f(2, 3, 4)
ele(2, 3, 4)
.callback
emy_callback
parciais são incrivelmente úteis.
Por exemplo, em uma sequência de chamadas de função "alinhadas por canal" (na qual o valor retornado de uma função é o argumento passado para a próxima).
Às vezes, uma função nesse pipeline requer um único argumento , mas a função imediatamente a montante dele retorna dois valores .
Nesse cenário,
functools.partial
pode permitir que você mantenha esse pipeline de funções intacto.Aqui está um exemplo específico e isolado: suponha que você queira classificar alguns dados pela distância de cada ponto de dados de algum destino:
Para classificar esses dados por distância do alvo, o que você gostaria de fazer é:
mas você não pode - o parâmetro- chave do método de classificação aceita apenas funções que usam um único argumento.
reescreva
euclid_dist
como uma função usando um único parâmetro:p_euclid_dist
agora aceita um único argumento,então agora você pode classificar seus dados passando a função parcial para o argumento-chave do método de classificação:
Ou, por exemplo, um dos argumentos da função muda em um loop externo, mas é corrigido durante a iteração no loop interno. Ao usar um parcial, você não precisa passar o parâmetro adicional durante a iteração do loop interno, porque a função modificada (parcial) não exige isso.
crie uma função parcial (usando a palavra-chave arg)
você também pode criar uma função parcial com um argumento posicional
mas isso será lançado (por exemplo, criando parcial com argumento de palavra-chave e depois chamando usando argumentos posicionais)
outro caso de uso: escrever código distribuído usando a
multiprocessing
biblioteca do python . Um conjunto de processos é criado usando o método Pool:Pool
possui um método de mapa, mas são necessárias apenas uma iterável; portanto, se você precisar transmitir uma função com uma lista de parâmetros mais longa, redefina a função como parcial, para corrigir todos, exceto um:fonte
resposta curta,
partial
fornece valores padrão aos parâmetros de uma função que, de outra forma, não teria valores padrão.fonte
partial
e assim por dianteParciais podem ser usados para criar novas funções derivadas que têm alguns parâmetros de entrada pré-atribuídos
Para ver o uso real de parciais no mundo real, consulte esta publicação realmente boa no blog:
http://chriskiehl.com/article/Cleaner-coding-through-partially-applied-functions/
Um simples, mas o exemplo de iniciante puro do blog, covers como alguém pode usar
partial
nore.search
para tornar o código mais legível.re.search
A assinatura do método é:Ao aplicar
partial
, podemos criar várias versões da expressão regularsearch
para atender aos nossos requisitos, por exemplo:Agora
is_spaced_apart
eis_grouped_together
são duas novas funções derivadasre.search
que têm opattern
argumento aplicado (já quepattern
é o primeiro argumento nare.search
assinatura do método).A assinatura dessas duas novas funções (solicitadas) é:
É assim que você pode usar essas funções parciais em algum texto:
Você pode consultar o link acima para obter uma compreensão mais aprofundada do assunto, pois abrange este exemplo específico e muito mais.
fonte
is_spaced_apart = re.compile('[a-zA-Z]\s\=').search
? Em caso afirmativo, existe uma garantia de que opartial
idioma compila a expressão regular para uma reutilização mais rápida?Na minha opinião, é uma maneira de implementar o curry em python.
O resultado é 3 e 4.
fonte
Também vale mencionar que quando a função parcial passou em outra função em que queremos "codificar" alguns parâmetros, esse deve ser o parâmetro mais à direita
mas se fizermos o mesmo, mas mudar um parâmetro
ele lançará o erro "TypeError: func () obteve vários valores para o argumento 'a'"
fonte
prt=partial(func, 7)
Esta resposta é mais um exemplo de código. Todas as respostas acima dão boas explicações sobre por que se deve usar parcial. Vou dar minhas observações e casos de uso sobre parcial.
A saída do código acima deve ser:
Observe que no exemplo acima foi retornada uma nova chamada que aceitará o parâmetro (c) como argumento. Observe que também é o último argumento para a função.
A saída do código acima também é:
Observe que * foi usado para descompactar os argumentos que não são de palavra-chave e a chamada retornada em termos de qual argumento ele pode usar é o mesmo que acima.
Outra observação é: O exemplo abaixo demonstra que retornos parciais podem ser chamados e que tomarão o parâmetro não declarado (a) como argumento.
A saída do código acima deve ser:
Similarmente,
Impressões acima do código
Eu tive que usá-lo quando eu estava usando o
Pool.map_async
método domultiprocessing
módulo. Você pode passar apenas um argumento para a função worker, então eu tive que usarpartial
para fazer com que minha função worker parecesse passível de chamada com apenas um argumento de entrada, mas, na realidade, minha função worker tinha vários argumentos de entrada.fonte