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 yield
computa 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 consumidor
msg = (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