Forma de modificar o comportamento (externo) de classes e funções.
def llog(f):
def wrapper(*args):
print("entrada:",args)
x = f(*args)
print("saida:",x)
return x
return wrapper
def aux(x,y):
return 2*x+y
zz = llog(aux)
aux = llog(aux)
@llog
def aux(x,y):
return 2*x+y
Voce pode retornar um objeto. se o objeto for chamado como função, o método __call__ desse objeto será executado
class decconta:
def __init__(self,f):
self.funcao=f
self.conta=0
def __call__(self,*args):
self.conta += 1
return self.funcao(*args)
xx=decconta(aux)
class e static methods https://realpython.com/instance-class-and-static-methods-demystified/ são decorators.
Iterator é um thunk do haskell - uma promessa de computação que retorna um elemento por vez.
Funciona dentro de um for
__iter__ que retorna um interator. A funçao iter chama o metodo __iter____next__ que retorna o proximo elemento. A função next chama o metodo __next__StopIteration quando não há mais elementosx = iter([2,3])
next(x)
next(x)
next(x)
for x in coisa:
...
é na verdade uma abreviação para
ii=iter(coisa)
try:
while True:
x = ii.__next__()
...
except StopIteration:
pass
class Repetidor:
def __init__(self,x,n=4):
self.n=n
self.x=x
def __iter__(self):
return self
def __next__(self):
if self.n<=0:
raise StopIteration
else:
self.n-=1
return self.x
Generators são funções que guardam o estado entre uma chamada e outra. Sintaticamente a unica diferença é usar um yield em vez do return
A cada execuçao, o yieldcomputa o proximo valor a ser retornado e interrompe a execuçao. No proximo next o generator continua executando do ultimo yield.
def rep(x,n=4):
while n>0:
n-=1
yield x
>>> z = rep(77)
>>> z.__next__()
77
>>> next(z)
77
>>> next(z)
77
>>> next(z)
77
>>> next(z)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
( e )zz= (x*x for x in range(10,1000) if x % 4 == 0)
zz.__next__()
next(zz)
uma biblioteca para iterators https://docs.python.org/3/library/itertools.html
iterators no Python não sao exatamente bem conectados com o resto do python como os thunks sao em haskell (na minha opinião).
a = [6,5,3,1,7]
sorted(a)
reversed(a)
== explora toda as 2 listas para ver se são iguas, mas nao iteratorslist(range(1:4))
[1,2,3] == range(1,4)
por outro lado faz algum sentido no expandir os iterators - eles vao gerando novos elementos e não dá para rewind/ voltar atras num iterator (por exemplo o iterator que le linhas de um arquivo)!
processos que enviam mensagens de um para outro. Normalmente produtores, filtros e consumidores
um filtro precisa receber uma mensagem, e talvez enviar uma outra mensagem para o consumidor final
consumidor.send(msg) - manda a mensagem msg para o objeto consumidormsg = (yield)def filtro(padrao,proximo):
print("Comecando filtro")
while True:
msg = (yield)
if padrao in msg:
proximo.send(msg)
def consumidor():
print("Comecando consumidor final")
while True:
l=(yield)
print(l)
c=consumidor()
f=filtro("abc",c)
c.__next__() #para chegar no yield
f.__next__() # para chegar no yield
for x in "afh ahabcj agdb yyabctt abc abdddc".split():
f.send(x)
Sem usar a itertools
pares: dado um iterator, retorna um iterator com os elementos nas posicoes pares (0,2,..)reverte: dado um iterator, reverte ele (obviamente é preciso expandir of interator para chegar no ultimo elemento - * )ziper: dado 2 iterators, retorna um iterator que retorna os elementos intercaladoscart: dado 2 iterators, retorna um iterator com o produto cartesiano dos elementos (todos os pares entre os 2 iterators) *ciclo: dado um iterator, retorna os elementos num ciclo infinitorangeinf(init,passo=1): retorna um iterator que gera numeros de init ate infinito, com passotake: como o take do haskelldrop - como o drop do haskell