regras - Encadeamento de funções em Python




regras python (4)

No codewars.com, encontrei a seguinte tarefa:

Crie uma função que adicione números quando chamados em sucessão. Então add(1) deve retornar 1 , add(1)(2) deve retornar 1+2 , ...

Embora eu esteja familiarizado com o básico do Python, nunca encontrei uma função que possa ser chamada em tal sucessão, ou seja, uma função f(x) que pode ser chamada como f(x)(y)(z)... Até agora, nem tenho certeza de como interpretar essa notação.

Como matemático, eu suspeitaria que f(x)(y) é uma função que atribui a cada x uma função g_{x} e depois retorna g_{x}(y) e da mesma forma para f(x)(y)(z)

Se essa interpretação estiver correta, o Python me permitirá criar dinamicamente funções que me parecem muito interessantes. Pesquisei na web na última hora, mas não consegui encontrar um caminho na direção certa. Como não sei como esse conceito de programação é chamado, no entanto, isso pode não ser muito surpreendente.

Como você chama esse conceito e onde posso ler mais sobre isso?


A maneira pitônica de fazer isso seria usar argumentos dinâmicos:

def add(*args):
    return sum(args)

Esta não é a resposta que você está procurando, e você pode saber disso, mas achei que daria de qualquer maneira, porque se alguém estivesse pensando em fazer isso não por curiosidade, mas por trabalho. Eles provavelmente deveriam ter a resposta "certa a fazer".


Eu não sei se isso é encadeamento de funções tanto quanto é encadeado que pode ser chamado, mas, como as funções são chamadas, acho que não há mal algum. De qualquer forma, há duas maneiras de pensar em fazer isso:

Subclassificação int e definição de __call__ :

A primeira maneira seria com uma subclasse int personalizada que define __call__ que retorna uma nova instância de si mesma com o valor atualizado:

class CustomInt(int):
    def __call__(self, v):
        return CustomInt(self + v)

A add função agora pode ser definida para retornar uma instância CustomInt , que, como uma chamada que retorna um valor atualizado próprio, pode ser chamada em sucessão:

>>> def add(v):
...    return CustomInt(v)
>>> add(1)
1
>>> add(1)(2)
3
>>> add(1)(2)(3)(44)  # and so on..
50

Além disso, como uma subclasse int , o valor retornado mantém o comportamento __str__ e __str__ de int s. Porém, para operações mais complexas, você deve definir outros dunders adequadamente .

Como o @Caridorc observou em um comentário, o add também pode ser simplesmente escrito como:

add = CustomInt 

Renomear a classe a ser add vez de CustomInt também funciona da mesma forma.

Defina um fechamento, requer uma chamada extra para gerar valor:

A única outra maneira em que consigo pensar envolve uma função aninhada que requer uma chamada de argumento extra vazia para retornar o resultado. Não estou usando nonlocal e opto por anexar atributos aos objetos de função para torná-lo portátil entre Pythons:

def add(v):
    def _inner_adder(val=None):  
        """ 
        if val is None we return _inner_adder.v 
        else we increment and return ourselves
        """
        if val is None:    
            return _inner_adder.v
        _inner_adder.v += val
        return _inner_adder
    _inner_adder.v = v  # save value
    return _inner_adder 

Isso retorna continuamente a si próprio ( _inner_adder ) que, se um val é fornecido, o incrementa ( _inner_adder += val ) e, se não, retorna o valor como está. Como mencionei, ele requer uma chamada extra () para retornar o valor incrementado:

>>> add(1)(2)()
3
>>> add(1)(2)(3)()  # and so on..
6

Se você deseja definir uma função a ser chamada várias vezes, primeiro você precisa retornar um objeto que pode ser chamado a cada vez (por exemplo, uma função); caso contrário, você deve criar seu próprio objeto, definindo um atributo __call__ para que ele possa ser chamado. .

O próximo ponto é que você precisa preservar todos os argumentos, o que, neste caso, significa que você pode querer usar Coroutines ou uma função recursiva. Mas observe que as corotinas são muito mais otimizadas / flexíveis do que as funções recursivas , especialmente para essas tarefas.

Aqui está uma função de exemplo usando Coroutines, que preserva o último estado de si mesmo. Observe que ele não pode ser chamado várias vezes, pois o valor de retorno é um integer que não pode ser chamado, mas você pode pensar em transformar isso no objeto esperado ;-).

def add():
    current = yield
    while True:
        value = yield current
        current = value + current


it = add()
next(it)
print(it.send(10))
print(it.send(2))
print(it.send(4))

10
12
16

Simplesmente:

class add(int):
   def __call__(self, n):
      return add(self + n)






python-3.x