Tutorial Adaptatividade em Python Danilo J. S. Bellini – @danilobellini – Tutorial: Adaptatividade em Python Workshop de Tecnologia Adaptativa – São Paulo – SP – 2015-01-29 e 2015-01-30 Python ● Multiparadigma ● Uso geral (científico, GUI, Web, games, etc.) ● Metaprogramação e reflexão – ● “Python is more dynamic, does less error-checking” (P. Norvig, ao comparar Python com LISP) Em tempo de execução... – Código (arquivos/strings) – AST – Bytecode ! s i a m o t i u E m =) Danilo J. S. Bellini – @danilobellini – Tutorial: Adaptatividade em Python Workshop de Tecnologia Adaptativa – São Paulo – SP – 2015-01-29 e 2015-01-30 Parte 1 Geradores, iteradores e iteráveis “Talk is cheap. Show me the code.” (Linus Torvalds) Danilo J. S. Bellini – @danilobellini – Tutorial: Adaptatividade em Python Workshop de Tecnologia Adaptativa – São Paulo – SP – 2015-01-29 e 2015-01-30 Avaliação tardia (deferred) e geradores ● ● ● ● Yield – Return-like – Estado “suspenso” Avaliação no uso, não na declaração “Fluxo de controle” em um objeto Iteráveis sem tamanho definido – I/O – “Algoritmo” sem término definido? ## Função Função geradora geradora def def count(start=0, count(start=0, step=1): step=1): while True: while True: yield yield start start start += start += step step ## Geradores Geradores são são iteradores iteradores gen = count() gen = count() next(gen) next(gen) next(gen) next(gen) next(gen) next(gen) gen_impares gen_impares == count(1, count(1, step=2) step=2) next(gen_impares) next(gen_impares) next(gen_impares) next(gen_impares) next(gen_impares) next(gen_impares) next(gen) next(gen) ## Expressão Expressão geradora geradora gg == (el (el ** ** 22 for for el el in in count(3) count(3) if el % 3 == if el % 3 == 0) 0) next(g) next(g) next(g) next(g) next(g) next(g) ?!? # Iteradores são iteráveis # Iteradores são iteráveis for for el el in in count(5, count(5, 3): 3): print(el) o: t print(el) n if pro nt if el el >= >= 30: 30: m te s.cou break o s break ol Is Danilo J. S. Bellini – @danilobellini – Tutorial: Adaptatividade em Python Workshop de Tecnologia Adaptativa – São Paulo – SP – 2015-01-29 e 2015-01-30 to iter In In [1]: [1]: def def count(start=0, count(start=0, step=1): step=1): ...: while True: ...: while True: ...: yield ...: yield start start ...: start += ...: start += step step ...: ...: In In [11]: [11]: gg == (el (el ** ** 22 for for el el in in count(3) count(3) ...: if el % 3 == ...: if el % 3 == 0) 0) ...: ...: In In [2]: [2]: gen gen == count() count() In In [12]: [12]: next(g) next(g) Out[12]: 9 Out[12]: 9 In In [3]: [3]: next(gen) next(gen) Out[3]: 0 Out[3]: 0 In In [13]: [13]: next(g) next(g) Out[13]: 36 Out[13]: 36 In In [4]: [4]: next(gen) next(gen) Out[4]: 1 Out[4]: 1 In In [14]: [14]: next(g) next(g) Out[14]: 81 Out[14]: 81 In In [15]: [15]: for for el el in in count(5, count(5, 3): 3): ....: print(el) ....: print(el) ....: if ....: if el el >= >= 30: 30: ....: break In [6]: gen_impares = count(1, step=2) ....: break In [6]: gen_impares = count(1, step=2) ....: ....: 5 In [7]: next(gen_impares) 5 In [7]: next(gen_impares) 8 Out[7]: 8 Out[7]: 11 11 11 14 In 14 In [8]: [8]: next(gen_impares) next(gen_impares) 17 Out[8]: 3 17 Out[8]: 3 20 20 23 In [9]: next(gen_impares) 23 In [9]: next(gen_impares) 26 Out[9]: 26 Out[9]: 55 29 29 32 In [10]: next(gen) 32 In [10]: next(gen) Out[10]: 3 Out[10]: 3 Danilo J. S. Bellini – @danilobellini – Tutorial: Adaptatividade em Python Workshop de Tecnologia Adaptativa – São Paulo – SP – 2015-01-29 e 2015-01-30 In In [5]: [5]: next(gen) next(gen) Out[5]: 2 Out[5]: 2 from from collections collections import import Iterator Iterator class class Counter(Iterator): Counter(Iterator): def def __init__(self, __init__(self, start=0, start=0, step=1): step=1): self.value = start self.value = start self.step self.step == step step self.finish = self.finish = False False def def next(self): next(self): if if self.finish: self.finish: raise raise StopIteration StopIteration result = self.value result = self.value self.value self.value += += self.step self.step return result return result Iterador/iterável com um pouco de orientação a objetos __next__ __next__ == next next ## Compatibilidade Compatibilidade Python Python 2/3 2/3 ● “Dunders” (Double UNDERscore) – __init__ ● – __next__ (next no Python 2) ● – Inicializador (“construtor”) Devolve o próximo elemento __iter__ ● ● Iterável: Devolve um novo iterador Iterador: Devolve a si próprio Mudança de comportamento do iterável dentro de seu laço gg == Counter() Counter() next(g) next(g) next(g) next(g) g.value g.value == 13 13 next(g) next(g) next(g) next(g) g.step g.step == 77 next(g) next(g) next(g) next(g) next(g) next(g) counter counter == Counter(start=-5, Counter(start=-5, step=7) step=7) for el in counter: for el in counter: print(el) print(el) counter.step counter.step -= -= 11 counter.finish counter.finish == counter.value counter.value << -10 -10 Danilo J. S. Bellini – @danilobellini – Tutorial: Adaptatividade em Python Workshop de Tecnologia Adaptativa – São Paulo – SP – 2015-01-29 e 2015-01-30 In In [1]: [1]: %paste %paste from collections from collections import import Iterator Iterator ## [...] [...] ## ## --- End End pasted pasted text text --In In [2]: [2]: gg == Counter() Counter() In In [3]: [3]: next(g) next(g) Out[3]: Out[3]: 00 In In [4]: [4]: next(g) next(g) Out[4]: 1 Out[4]: 1 In In [5]: [5]: g.value g.value == 13 13 In In [6]: [6]: next(g) next(g) Out[6]: 13 Out[6]: 13 In In [7]: [7]: next(g) next(g) Out[7]: 14 Out[7]: 14 In In [8]: [8]: g.step g.step == 77 In In [9]: [9]: next(g) next(g) Out[9]: 15 Out[9]: 15 In In [10]: [10]: next(g) next(g) Out[10]: 22 Out[10]: 22 Classe “Counter” In In [12]: [12]: counter counter == Counter(start=-5, Counter(start=-5, step=7) step=7) In In [13]: [13]: for for el el in in counter: counter: ....: print(el) ....: print(el) ....: counter.step ....: counter.step -= -= 11 ....: counter.finish ....: counter.finish == counter.value counter.value << -10 -10 ....: ....: -5 -5 22 88 13 13 17 17 ## [...] [...] Lembram Lembram do do Counter.next? Counter.next? 20 20 def next(self): def next(self): 22 22 if if self.finish: self.finish: 23 23 raise raise StopIteration StopIteration 23 23 result = self.value result = self.value 22 22 self.value self.value += += self.step self.step 20 20 return result return result 17 17 13 13 88 22 -5 -5 In next(g) In [11]: [11]: next(g) Danilo J. S. Bellini – @danilobellini – Tutorial: Adaptatividade em Python Out[11]: 29 Out[11]: 29 Workshop de Tecnologia Adaptativa – São Paulo – SP – 2015-01-29 e 2015-01-30 Lazy evaluation Avaliação “preguiçosa” ● “Tardia” + “cache”: ● “Por que fazer antes o que pode ser deixado para a última hora?” + Valores computados uma única vez ● Uso único? – Único caso equivalente à avaliação tardia (“deferred”) ● Iteradores/Geradores: cópias (T) – itertools.tee – audiolazy.thub – Fluxo de dados e “Dataflow programming” Funções (+ imutabilidade): decorators (cache) – functools.lru_cache (Python 3) – audiolazy.cached Danilo J. S. Bellini – @danilobellini – Tutorial: Adaptatividade em Python Workshop de Tecnologia Adaptativa – São Paulo – SP – 2015-01-29 e 2015-01-30 ## coding: coding: utf-8 utf-8 from itertools from itertools import import count, count, takewhile, takewhile, tee tee def def prime_gen(): prime_gen(): """ """ Gerador Gerador de de números números primos primos """ """ yield yield 22 primes primes == [] [] for for value value in in count(start=3, count(start=3, step=2): step=2): iter_primes = takewhile(lambda iter_primes = takewhile(lambda x: x: xx ** xx <= <= value, value, primes) primes) if all(value % p != 0 for p in iter_primes): if all(value % p != 0 for p in iter_primes): primes.append(value) 1º primes.append(value) 1º primo: primo: 22 yield 2º yield value value 2º primo: primo: 33 3º 3º primo: primo: 55 primes, 4º primes, primes_copy primes_copy == tee(prime_gen(), tee(prime_gen(), 2) 2) 4º primo: primo: 77 5º 5º primo: primo: 11 11 for idx, p in enumerate(primes, 1): [...] for idx, p in enumerate(primes, 1): [...] print(u"{:>5}º primo: {}".format(idx, p)) 198º print(u"{:>5}º primo: {}".format(idx, p)) 198º primo: primo: 1213 1213 if idx == 200: 199º primo: 1217 if idx == 200: 199º primo: 1217 break 200º break 200º primo: primo: 1223 1223 for for idx, idx, pp in in enumerate(primes_copy, enumerate(primes_copy, 1): 1): print(u"{:>5}º primo ao quadrado: print(u"{:>5}º primo ao quadrado: {}".format(idx, {}".format(idx, pp ** ** 2)) 2)) if idx == 200: if idx == 200: 1º 1º primo primo ao ao quadrado: quadrado: 44 break break 2º 2º primo primo ao ao quadrado: quadrado: 99 3º 3º primo primo ao ao quadrado: quadrado: 25 25 4º primo ao quadrado: 49 4º primo ao quadrado: 49 Baseado em: 5º 5º primo primo ao ao quadrado: quadrado: 121 121 https://gist.github.com/danilobellini/7233352 [...] [...] 198º 198º primo primo ao ao quadrado: quadrado: 1471369 1471369 199º primo ao quadrado: 1481089 199º primo ao quadrado: 1481089 Danilo J. S. Bellini – @danilobellini – Tutorial: Adaptatividade em primo Python 200º ao 200ºe primo ao quadrado: quadrado: 1495729 1495729 Workshop de Tecnologia Adaptativa – São Paulo – SP – 2015-01-29 2015-01-30 ## coding: coding: utf-8 utf-8 from audiolazy from audiolazy import import count, count, takewhile, takewhile, thub thub def def prime_gen(): prime_gen(): """ """ Gerador Gerador de de números números primos primos """ """ yield yield 22 primes primes == [] [] for for value value in in count(start=3, count(start=3, step=2): step=2): stream_primes = takewhile(lambda stream_primes = takewhile(lambda x: x: xx ** xx <= <= value, value, primes) primes) if all(value % stream_primes != 0): if all(value % stream_primes != 0): primes.append(value) 1º primes.append(value) 1º primo: primo: 22 yield 2º yield value value 2º primo: primo: 33 3º 3º primo: primo: 55 primes 4º primes == thub(prime_gen(), thub(prime_gen(), 2) 2) 4º primo: primo: 77 5º 5º primo: primo: 11 11 for idx, p in enumerate(primes, 1): [...] for idx, p in enumerate(primes, 1): [...] print(u"{:>5}º primo: {}".format(idx, p)) 198º print(u"{:>5}º primo: {}".format(idx, p)) 198º primo: primo: 1213 1213 if idx == 200: 199º primo: 1217 if idx == 200: 199º primo: 1217 break 200º break 200º primo: primo: 1223 1223 for for idx, idx, pp in in enumerate(primes, enumerate(primes, 1): 1): print(u"{:>5}º primo ao quadrado: print(u"{:>5}º primo ao quadrado: {}".format(idx, {}".format(idx, pp ** ** 2)) 2)) if idx == 200: if idx == 200: 1º 1º primo primo ao ao quadrado: quadrado: 44 break break 2º 2º primo primo ao ao quadrado: quadrado: 99 3º 3º primo primo ao ao quadrado: quadrado: 25 25 Mesma coisa, mas com 4º primo ao quadrado: 49 4º primo ao quadrado: 49 audiolazy.thub no lugar de 5º 5º primo primo ao ao quadrado: quadrado: 121 121 itertools.tee, e takewhile [...] [...] devolvendo audiolazy.Stream 198º 198º primo primo ao ao quadrado: quadrado: 1471369 1471369 199º primo ao quadrado: 1481089 199º primo ao quadrado: 1481089 Danilo J. S. Bellini – @danilobellini – Tutorial: Adaptatividade em primo Python 200º ao 200ºe primo ao quadrado: quadrado: 1495729 1495729 Workshop de Tecnologia Adaptativa – São Paulo – SP – 2015-01-29 2015-01-30 Outros exemplos ● ● Geradores para I/O – AudioLazy: mcfm.py, robotize.py, animated_plot.py – Turing(1936): a-machine VS c-machine Avaliação lazy/preguiçosa por decorator import import sys sys ## Para Para funcionar funcionar em em Python Python 22 ee 33 if if sys.version_info.major sys.version_info.major == == 2: 2: from cachetools import lru_cache from cachetools import lru_cache else: else: from from functools functools import import lru_cache lru_cache Algoritmo para cada valor/bloco de “entrada” (saída em tempo finito); causalidade @lru_cache(maxsize=1000) @lru_cache(maxsize=1000) def def fib(n): fib(n): return return nn if if nn <= <= 11 else else fib(n fib(n -- 2) 2) ++ fib(n fib(n -- 1) 1) print(fib(500)) print(fib(500)) 139423224561697880139724382870407283950070256587697307264108962948325571622863290691557658876222521294125 139423224561697880139724382870407283950070256587697307264108962948325571622863290691557658876222521294125 Danilo J. S. Bellini – @danilobellini – Tutorial: Adaptatividade em Python Workshop de Tecnologia Adaptativa – São Paulo – SP – 2015-01-29 e 2015-01-30 Parte 2 “Primeira classe” e closures Danilo J. S. Bellini – @danilobellini – Tutorial: Adaptatividade em Python Workshop de Tecnologia Adaptativa – São Paulo – SP – 2015-01-29 e 2015-01-30 Design patterns? (design strategies?) ● Slides “Design Patterns in Dynamic Programming” (P. Norvig) – ● Defaults != patterns != standards != defaults – ● “16 of 23 patterns have qualitatively simpler implementation in Lisp or Dylan than in C++ for at least some uses of each pattern” http://info.abril.com.br/noticias/rede/gestao20/software/a-lingua-portuguesa-brasil eira-e-pessima-standard-vs-pattern/ 2013-2014: Grupo de estudos para discutir aplicabilidade em linguagens dinâmicas: – https://garoa.net.br/wiki/Design_patterns_em_linguagens_din%C3%A2micas – Simplificados com funções/“tipos” de primeira classe: ● ● Command, strategy, template method, visitor, abstract factory, factory method, flyweight, proxy, chain of responsibility, state Exemplos (código) em várias linguagens – http://en.wikibooks.org/wiki/Computer_Science_Design_Patterns Danilo J. S. Bellini – @danilobellini – Tutorial: Adaptatividade em Python Workshop de Tecnologia Adaptativa – São Paulo – SP – 2015-01-29 e 2015-01-30 Função de primeira classe ● ● Funções como valores/objetos Exemplo: pattern strategy para operador binário com o símbolo como parâmetro sem usar “classes” – Baseado em (o link possui uma versão em C e várias em Python): https://github.com/danilobellini/design_patterns def def add(x, add(x, y): y): return return xx ++ yy def def sub(x, sub(x, y): y): return return xx -- yy def def mod(x, mod(x, y): y): return return xx %% yy ops ops == {{ "+": "+": add, add, "-": sub, "-": sub, "%": "%": mod, mod, }} Alte rnat ivas def def apply_op(symbol, apply_op(symbol, x, x, y): y): return ops[symbol](x, return ops[symbol](x, y) y) ops ops == {{ "+": "+": lambda lambda x, x, y: y: xx ++ y, y, "-": "-": lambda lambda x, x, y: y: xx -- y, y, "%": lambda x, y: x % y, "%": lambda x, y: x % y, }} template template == "lambda "lambda x, x, y: y: xx %s %s y" y" ops = {s: eval(template % s) for ops = {s: eval(template % s) for ss in in "+-*/%"} "+-*/%"} print(apply_op("+", print(apply_op("+", 2, 2, 3)) 3)) ## 55 print(apply_op("-", 7)) print(apply_op("-", 5, 7)) ## -2 -2 Danilo J. S. Bellini –5, @danilobellini – Tutorial: Adaptatividade em Python print(apply_op("%", 22, 13)) # 9 print(apply_op("%", 22, 13)) –#São 9 Paulo – SP – 2015-01-29 e 2015-01-30 Workshop de Tecnologia Adaptativa [Lexical] Closure ● “Função” + “contexto” Blocos “instanciáveis” (e.g. funções anônimas) + (com) “Variáveis livres” (definidas para cada “instância”) ● Caso típico: aplicação parcial def def adder(a): adder(a): return return lambda lambda b: b: bb ++ aa add2 add2 == adder(2) adder(2) sub1 = adder(-1) sub1 = adder(-1) print(add2(15)) ## 17 print(add2(15)) 17 print(sub1(4)) ## 33 print(sub1(4)) print(add2(sub1(8))) print(add2(sub1(8))) ## 99 “b” é o único parâmetro da função anônima devolvida, “a” é uma variável livre Danilo J. S. Bellini – @danilobellini – Tutorial: Adaptatividade em Python Workshop de Tecnologia Adaptativa – São Paulo – SP – 2015-01-29 e 2015-01-30 Quantidades e nomes de parâmetros em funções/métodos ● def/lambda – ● ● Posicionais – ● 2. (1x) * unário, continuação dos argumentos posicionais com qualquer iterável Pertencem à função/método Mutabilidade → influencia em todas as chamadas Argumentos extras * unário → tupla Nominados (keyword) – ** unário → dicionário Chamada (todo item é opcional) 1. Argumentos posicionais Valores default com “=” ● – ● 3. Pares “chave=valor” explícitos 4. (1x) ** unário, utiliza todos os pares “chave=valor” de um dicionário ● * e ** unários da chamada não são os mesmos recebidos pela função como argumentos – Verificação dos nomes, quantidades, excessos, ausências e repetições (TypeError) def def smaller_first(pair): smaller_first(pair): k, k, vv == pair pair return len(k), return len(k), kk def def show_it_all(a, show_it_all(a, b, b, c=None, c=None, *args, *args, **kwargs): **kwargs): for k, v in sorted(locals().items(), Danilo J. S. for Bellinik, – @danilobellini – Tutorial: Adaptatividade key=smaller_first): em Python v in sorted(locals().items(), key=smaller_first): print("{:>6}: {}".format(k, v)) Workshop de Tecnologia Adaptativa – São Paulo – SP – 2015-01-29 e 2015-01-30 print("{:>6}: {}".format(k, v)) squares squares == [i [i ** ** 22 for for ii in in range(10)] range(10)] ascii = {ch: hex(ord(ch)) for ascii = {ch: hex(ord(ch)) for ch ch in in "Place"} "Place"} >>> >>>show_it_all(0, show_it_all(0,-1) -1) a: 0 a: 0 b: b:-1 -1 c: c: None None args: () args: () kwargs: kwargs: {} {} >>> >>>show_it_all(*"First") show_it_all(*"First") a: a: FF b: b: ii c: c: rr args: args:('s', ('s','t') 't') kwargs: {} kwargs: {} >>> >>>show_it_all(b=0, show_it_all(b=0,a=-1) a=-1) a: -1 a: -1 b: b:00 c: c: None None args: () args: () kwargs: kwargs: {} {} >>> >>>show_it_all(*squares) show_it_all(*squares) a: 0 a: 0 b: b:11 c: c:44 args: args:(9, (9,16, 16,25, 25,36, 36,49, 49,64, 64,81) 81) kwargs: {} kwargs: {} >>> >>>show_it_all(1, show_it_all(1,2, 2,3, 3,4, 4,5, 5,d=415) d=415) a: 1 a: 1 b: >>> b:22 >>>show_it_all(-5, show_it_all(-5,*squares, *squares,args=415) args=415) c: 3 a: -5 c: 3 a: -5 args: (4, 5) b: args: (4, 5) b:00 kwargs: c: kwargs:{'d': {'d':415} 415} c:11 args: args:(4, (4,9, 9,16, 16,25, 25,36, 36,49, 49,64, 64,81) 81) kwargs: {'args': 415} >>> show_it_all(b="Second", **ascii) kwargs: {'args': 415} >>> show_it_all(b="Second", **ascii) a: a:0x61 0x61 b: ## TypeError b: Second Second TypeError show_it_all("First", c: 0x63 show_it_all("First", **ascii) **ascii) ## "a" "a" 2x 2x c: 0x63 args: () show_it_all(*squares, **ascii) # "a" 2x args: () show_it_all(*squares, **ascii) # "a" 2x show_it_all(1, kwargs: {'P': '0x50', 'e': '0x65', 'l': '0x6c'} show_it_all(1, 2, 2, 3, 3, 4, 4, 5, 5, b=5) b=5) ## "b" "b" 2x 2x kwargs: {'P': '0x50', 'e': '0x65', 'l': '0x6c'} show_it_all(b=5, e=0, **ascii) # "e" 2x show_it_all(b=5, e=0, **ascii) # "e" 2x show_it_all(**ascii) show_it_all(**ascii) ## "b" "b" ausente ausente def smaller_first(pair): def smaller_first(pair): show_it_all(0) # "b" ausente show_it_all(0) # "b" ausente k, k, vv == pair pair show_it_all() # show_it_all() # "a" "a" ausente ausente return len(k), k return len(k), k def def show_it_all(a, show_it_all(a, b, b, c=None, c=None, *args, *args, **kwargs): **kwargs): for k, v in sorted(locals().items(), key=smaller_first): for k, J.vS.in sorted(locals().items(), key=smaller_first): Danilo Bellini – @danilobellini – Tutorial: Adaptatividade em Python Quanta coisa... print("{:>6}: {}".format(k, v)) print("{:>6}: {}".format(k, v)) Workshop de Tecnologia Adaptativa – São Paulo – SP – 2015-01-29 e 2015-01-30 Decorator ## coding: coding: utf-8 utf-8 from functools from functools import import wraps, wraps, reduce reduce ● def def double_of(func): double_of(func): ● """Decorator que dobra o resultado da função.""" """Decorator que dobra o resultado da função.""" @wraps(func) @wraps(func) ## Copia Copia informações informações de de func func def wrapper(*args, **kwargs): def wrapper(*args, **kwargs): return return 22 ** func(*args, func(*args, **kwargs) **kwargs) return wrapper return wrapper sum_twice sum_twice == double_of(sum) double_of(sum) print(sum_twice([2, print(sum_twice([2, 5, 5, 3])) 3])) ## 20 20 @double_of @double_of def def prod_twice(data): prod_twice(data): return return reduce(lambda reduce(lambda x, x, y: y: xx ** y, y, data, data, 1) 1) print(prod_twice([2, print(prod_twice([2, 5, 5, 3])) 3])) ## 60 60 ● Função 1 parâmetro – 1 valor de saída – ● Função ou classe Normalmente função ou classe Uso com o @ Usa o nome do próprio objeto “decorado” – ## AA menos menos dos dos nomes, nomes, oo acima acima éé oo mesmo mesmo que: que: def def prod(data): prod(data): return return reduce(lambda reduce(lambda x, x, y: y: xx ** y, y, data, data, 1) 1) prod2x = double_of(prod) # Decorator! prod2x = double_of(prod) # Decorator! Danilo J. S. Bellini – @danilobellini – Tutorial: Adaptatividade em Python print(prod2x([2, 5, print(prod2x([2, 5, 3])) 3])) ## 60 60 Workshop de Tecnologia Adaptativa – São Paulo – SP – 2015-01-29 e 2015-01-30 Métodos com “self” explícito? ● Currying! – Aplicação parcial do primeiro parâmetro class class MsgKeeper(object): MsgKeeper(object): def def __init__(self, __init__(self, msg): msg): self.msg = msg self.msg = msg def def show(self): show(self): print(self.msg) print(self.msg) foo foo == MsgKeeper("foo") MsgKeeper("foo") bar = MsgKeeper("bar") bar = MsgKeeper("bar") foo.show() foo.show() bar.show() bar.show() MsgKeeper.show(foo) MsgKeeper.show(foo) MsgKeeper.show(bar) MsgKeeper.show(bar) ● Aninhamento – ● “self” é um nome arbitrário Classes de primeira classe (+ closure) – Exemplos: ● audiolazy.StrategyDict – ● Docstring dinâmica e por instância dose (tratamento de eventos do watchdog) Danilo J. S. Bellini – @danilobellini – Tutorial: Adaptatividade em Python Workshop de Tecnologia Adaptativa – São Paulo – SP – 2015-01-29 e 2015-01-30 class class MsgKeeper(object): MsgKeeper(object): def def __init__(self, __init__(self, msg): msg): self.msg self.msg == msg msg def show(self): def show(self): print(self.msg) print(self.msg) ## Definido Definido fora fora do do namespace namespace da da classe classe def prefixed_keeper(self): def prefixed_keeper(self): class class PrefixedKeeper(MsgKeeper): PrefixedKeeper(MsgKeeper): @property @property def def msg(this): msg(this): return return self.msg self.msg ++ this._msg this._msg @msg.setter @msg.setter def def msg(this, msg(this, value): value): this._msg this._msg == value value return return PrefixedKeeper PrefixedKeeper turn turn == MsgKeeper("turn") MsgKeeper("turn") O método prefixed_keeper não existia quando turn foi instanciado! Classe de primeira classe e Monkeypatch Monkeypatch: atualização da classe em tempo de execução ## Monkeypatch Monkeypatch MsgKeeper.prefixed_keeper MsgKeeper.prefixed_keeper == prefixed_keeper prefixed_keeper ## (poderia (poderia ser ser definido definido dentro dentro da da classe) classe) turno turno == turn.prefixed_keeper()("o") turn.prefixed_keeper()("o") turnon = turnon = turno.prefixed_keeper()("n") turno.prefixed_keeper()("n") turn.show() turn.show() ## turn turn turno.show() # turno turno.show() # turno turnon.show() turnon.show() ## turnon turnon turn.msg = "Pyth" turn.msg = "Pyth" turnon.show() turnon.show() ## Python Python Danilo J. S. Bellini – @danilobellini – Tutorial: Adaptatividade em Python Workshop de Tecnologia Adaptativa – São Paulo – SP – 2015-01-29 e 2015-01-30 Parte 3 Namespaces, dicionários e escopo (léxico) Danilo J. S. Bellini – @danilobellini – Tutorial: Adaptatividade em Python Workshop de Tecnologia Adaptativa – São Paulo – SP – 2015-01-29 e 2015-01-30 Dicionário da classe e dicionário da instância ● ● ● Tudo em Python é um objeto Namespaces são representados como dicionários Objetos e classes [normalmente] possuem um namespace de atributos – “vars(obj)” ● ● Dicionário (__dict__) Nem sempre é aplicável – – Tipos built-ins, __slots__ “dir(obj)” ● ● class class A(object): A(object): data data == "Testing" "Testing" def __init__(self, def __init__(self, data): data): self.data = data self.data = data other other == A("Other") A("Other") ## Digitar Digitar no no IPython: IPython: A.data A.data other.data other.data type(other) type(other) ## other.__class__ other.__class__ type(other).data type(other).data vars(other) vars(other) vars(A) vars(A) ## EE se se apagarmos apagarmos da da instância? instância? del other.data del other.data vars(other) vars(other) other.data other.data __slots__ = [“__dict_ _”] Lista de nomes (strings) dos atributos, incluindo os da classe/herança (exceto os da metaclasse) Sempre aplicável Danilo J. S. Bellini – @danilobellini – Tutorial: Adaptatividade em Python Workshop de Tecnologia Adaptativa – São Paulo – SP – 2015-01-29 e 2015-01-30 In In [1]: [1]: class class A(object): A(object): ...: data ...: data == "Testing" "Testing" ...: def __init__(self, ...: def __init__(self, data): data): ...: self.data = data ...: self.data = data ...: ...: In In [2]: [2]: other other == A("Other") A("Other") In In [3]: [3]: A.data A.data Out[3]: 'Testing' Out[3]: 'Testing' In In [4]: [4]: other.data other.data Out[4]: 'Other' Out[4]: 'Other' In In [5]: [5]: type(other) type(other) Out[5]: __main__.A Out[5]: __main__.A In In [6]: [6]: type(other).data type(other).data Out[6]: 'Testing' Out[6]: 'Testing' In In [7]: [7]: vars(other) vars(other) Out[7]: {'data': Out[7]: {'data': 'Other'} 'Other'} Resolução dinâmica de atributos In In [9]: [9]: del del other.data other.data Atributos do objeto (exceto para dunders) In In [10]: [10]: vars(other) vars(other) Out[10]: Out[10]: {} {} In In [11]: [11]: other.data other.data Out[11]: 'Testing' Out[11]: 'Testing' Atributos da classe Herança (MRO) In In [8]: [8]: vars(A) vars(A) Out[8]: Out[8]: <dictproxy <dictproxy {'__dict__': {'__dict__': <attribute <attribute '__dict__' '__dict__' of of 'A' 'A' objects>, objects>, '__doc__': None, '__doc__': None, '__init__': '__init__': <function <function __main__.__init__>, __main__.__init__>, '__module__': '__main__', '__module__': '__main__', '__weakref__': '__weakref__': <attribute <attribute '__weakref__' '__weakref__' of of 'A' 'A' objects>, objects>, 'data': 'Testing'}> Danilo'Testing'}> J. S. Bellini – @danilobellini – Tutorial: Adaptatividade em Python 'data': Workshop de Tecnologia Adaptativa – São Paulo – SP – 2015-01-29 e 2015-01-30 ? Docstrings, help ● Strings são imutáveis ● Primeira string (literal) do bloco – Módulo – Classe – Método/Função ● Built-in “help()” ● Dunder __doc__ – Referência fixa em classes ● – ● Dinâmico se __doc__ for uma property (e.g. audiolazy.StrategyDict) Referência modificável em módulos e métodos/funções (uso intenso na AudioLazy) Documentação no próprio código Dicas Dicas para para uso uso do do IPython IPython TAB -> TAB -> Code Code completion completion ?? ao final -> Docstring ao final -> Docstring ++ extras extras ?? ao final -> Código-fonte ?? ao final -> Código-fonte ## coding: coding: utf-8 utf-8 def is_palindrome(val): def is_palindrome(val): """ """ Verifica Verifica se se aa representação representação do do valor valor fornecido é um palíndromo fornecido é um palíndromo """ """ as_string as_string == repr(val) repr(val) return as_string return as_string == == as_string[::-1] as_string[::-1] ## Rodar Rodar em em um um REPL REPL is_palindrome(123213) is_palindrome(123213) ## False False is_palindrome(123321) # True is_palindrome(123321) # True help(is_palindrome) help(is_palindrome) ## Exibe Exibe aa docstring docstring ee ## outras outras informações informações is_palindrome.__doc__ is_palindrome.__doc__ ## Devolve Devolve aa docstring docstring #\n #\n #----Verifica #----Verifica se se aa representação representação do do valor\n valor\n #----fornecido é um palíndromo\n #----fornecido é um palíndromo\n #---#---- Danilo J. S. Bellini – @danilobellini – Tutorial: Adaptatividade em Python Workshop de Tecnologia Adaptativa – São Paulo – SP – 2015-01-29 e 2015-01-30 Locals & globals ● ● locals 2 namespaces por contexto (escopo léxico): “local” e “global” globals Funções built-in built-ins – locals() – globals() – dir() ● ● Resolução de nomes: Sem parâmetro fornece os nomes (chaves) de “locals()” Modificar o resultado de “locals()” apenas possui o efeito de mudança do namespace no nível do módulo/script (ou direto no REPL) from from math math import import factorial factorial for for ii in in range(35): range(35): locals()["f%d" locals()["f%d" %% i] i] == factorial(i) factorial(i) print(f0) print(f0) ## 11 print(f1) print(f1) ## 11 print(f5) print(f5) ## 120 120 print(f15) # print(f15) # 1307674368000 1307674368000 print(dir()) print(dir()) ## Como Como script, script, Python Python 2: 2: #['__builtins__', '__doc__', – Manipulação do namespace a partir de #['__builtins__', '__doc__', # strings e valores ao invés de nomes em # '__file__', '__file__', '__name__', '__name__', # '__package__', código # '__package__', 'f0', 'f0', 'f1', 'f1', 'f10', 'f10', ## 'f11', 'f11', 'f12', 'f12', 'f13', 'f13', 'f14', 'f14', 'f15', 'f15', Módulos audiolazy.lazy_math e # 'f16', 'f17', 'f18', 'f19', 'f2', audiolazy.lazy_itertools # 'f16', 'f17', 'f18', 'f19', 'f2', ## 'f20', 'f20', 'f21', 'f21', 'f22', 'f22', 'f23', 'f23', 'f24', 'f24', – Criação massiva / automação ## 'f25', 'f26', 'f27', 'f28', 'f29', 'f25', 'f26', 'f27', 'f28', 'f29', – Uso de dados ao invés de estrutura ## 'f3', 'f3', 'f30', 'f30', 'f31', 'f31', 'f32', 'f32', 'f33', 'f33', # 'f34', 'f4', 'f5', 'f6', 'f7', # 'f34', 'f4', 'f5', 'f6', 'f7', Danilo J. S. Bellini – @danilobellini – Tutorial: Adaptatividade em Python # 'f8', 'f9', 'factorial', 'f9', 'factorial', 'i'] 'i'] Workshop de Tecnologia Adaptativa – São Paulo – SP#– 'f8', 2015-01-29 e 2015-01-30 ● “Escopo dinâmico”? ## coding: coding: utf-8 utf-8 template = template = u"{level} u"{level} ({author}): ({author}): {msg}" {msg}" print(template.format( print(template.format( msg msg == u"Isto u"Isto não não éé uma uma mensagem.", mensagem.", level = u"Info", level = u"Info", author author == u"Danilo", u"Danilo", )) # Exibe: )) # Exibe: ## Info Info (Danilo): (Danilo): Isto Isto não não éé uma uma mensagem. mensagem. msg msg == u"Contexto u"Contexto como como parâmetro?" parâmetro?" level = u"Dinâmico" level = u"Dinâmico" author author == u"Locals!" u"Locals!" print(template.format(**locals())) print(template.format(**locals())) ## Exibe: Exibe: ## Dinâmico (Locals!): Contexto como parâmetro? Dinâmico (Locals!): Contexto como parâmetro? ● É possível a passagem do resultado de locals() como parâmetro – No exemplo, recebe como “**kwargs”, não como valores despejados no locals() externo. – Valores mutáveis (e.g. listas) podem ser modificados. Danilo J. S. Bellini – @danilobellini – Tutorial: Adaptatividade em Python Workshop de Tecnologia Adaptativa – São Paulo – SP – 2015-01-29 e 2015-01-30 Documentação automática ● ● audiolazy.StrategyDict – Dicionário de estratégias – Múltiplas implementações de “coisas similares” – Iterável pelas estratégias – Mutável – Callable (estratégia default) – Estratégias acessíveis como atributos e como itens – Docstring dinâmica (resumo das docstrings das estratégias) audiolazy.format_docstring – Decorator p/ fazer docstrings com a partir de template ● Sphinx (reflexão) – Geração de documentação em HTML, LaTeX, PDF, man pages, etc. – conf.py + reStructuredText + docstrings (em reStructuredText) – Equacionamentos matemáticos em LaTeX – Integração com Matplotlib Danilo J. S. Bellini – @danilobellini – Tutorial: Adaptatividade em Python Workshop de Tecnologia Adaptativa – São Paulo – SP – 2015-01-29 e 2015-01-30 audiolazy.format_docstring ## coding: coding: utf-8 utf-8 ## Código original Código original na na AudioLazy AudioLazy (exceto (exceto pela pela docstring): docstring): def def format_docstring(template_="{__doc__}", format_docstring(template_="{__doc__}", *args, *args, **kwargs): **kwargs): def decorator(func): def decorator(func): if if func.__doc__: func.__doc__: kwargs["__doc__"] kwargs["__doc__"] == func.__doc__.format(*args, func.__doc__.format(*args, **kwargs) **kwargs) func.__doc__ = template_.format(*args, **kwargs) func.__doc__ = template_.format(*args, **kwargs) return return func func return decorator return decorator ## Exemplo Exemplo def def adder(a): adder(a): @format_docstring(a=a) @format_docstring(a=a) def def add(b): add(b): """Efetua """Efetua aa soma soma {a} {a} ++ bb para para oo dado dado b.""" b.""" return a + b return a + b return return add add add3 add3 == adder(3) adder(3) sub2 = adder(-2) sub2 = adder(-2) help(add3) help(add3) ## Efetua Efetua aa soma soma 33 ++ bb para para oo dado dado b. b. help(sub2) help(sub2) ## Efetua Efetua aa soma soma -2 -2 ++ bb para para oo dado dado b. b. Danilo J. S. Bellini – @danilobellini – Tutorial: Adaptatividade em Python Workshop de Tecnologia Adaptativa – São Paulo – SP – 2015-01-29 e 2015-01-30 Parte 4 Fallback e reflexão (reflection) para itens e atributos Danilo J. S. Bellini – @danilobellini – Tutorial: Adaptatividade em Python Workshop de Tecnologia Adaptativa – São Paulo – SP – 2015-01-29 e 2015-01-30 Dunder __missing__ ● ● “Fallback” para tentativa de acessar item inexistente no dicionário Necessita de uma nova classe (herança) ## coding: coding: utf-8 utf-8 from __future__ from __future__ import import unicode_literals unicode_literals ## Vamos remover acentos! Vamos remover acentos! class class DictDefaultsToKey(dict): DictDefaultsToKey(dict): def def __missing__(self, __missing__(self, key): key): return key return key rev_ascii rev_ascii == {{ "a": "a": "áàãâāäă", "áàãâāäă", "A": "A": "ÁÀÃÂĀÄĂ", "ÁÀÃÂĀÄĂ", "e": "éè ẽ êēëĕ" , "E": "ÉÈ "e": "éèẽêēëĕ", "E": "ÉÈẼẼÊĒËĔ" ÊĒËĔ",, "i": "i": "íìĩîīïĭ "íìĩîīïĭ", ", "I": "I": "ÍÌĨÎĪÏĬ", "ÍÌĨÎĪÏĬ", "o": "óòõôōöŏ", "O": "ÓÒÕÔŌÖŎ", "o": "óòõôōöŏ", "O": "ÓÒÕÔŌÖŎ", "u": "u": "úùũûūüŭ", "úùũûūüŭ", "U": "U": "ÚÙŨÛŪÜŬ", "ÚÙŨÛŪÜŬ", "c": "C": "c": "ç", "ç", "C": "Ç", "Ç", "n": "ñ", "N": "Ñ", "n": "ñ", "N": "Ñ", }} ascii_dict ascii_dict == DictDefaultsToKey() DictDefaultsToKey() for k, values for k, values in in rev_ascii.items(): rev_ascii.items(): ascii_dict.update((v, ascii_dict.update((v, k) k) for for vv in in values) values) def def to_ascii(msg): to_ascii(msg): return return "".join(ascii_dict[ch] "".join(ascii_dict[ch] for for ch ch in in msg) msg) ## Exemplos Exemplos to_ascii("La to_ascii("La cédille: cédille: ç/Ç ç/Ç (Forçação (Forçação de de barra)") barra)") to_ascii("Qui a décidé d'être naïf?") to_ascii("Qui a décidé d'être naïf?") to_ascii("ñÑãÃsáÁüúù...") to_ascii("ñÑãÃsáÁüúù...") Danilo J. S. Bellini – @danilobellini – Tutorial: Adaptatividade em Python Workshop de Tecnologia Adaptativa – São Paulo – SP – 2015-01-29 e 2015-01-30 __getitem__, __call__ ## -*-*- coding: coding: utf-8 utf-8 -*-*from functools import from functools import reduce reduce from from operator operator import import add, add, mul mul ● class class SumProd(object): SumProd(object): def def __init__(self): __init__(self): self.count self.count == 00 def def __getitem__(self, __getitem__(self, key): key): ## 11 argumento! argumento! """Somatório (não-vazio)""" """Somatório (não-vazio)""" self.count self.count += += 11 return return reduce(add, reduce(add, key) key) def def __call__(self, __call__(self, *key): *key): """Produtório (não-vazio)""" """Produtório (não-vazio)""" self.count self.count += += 11 return return reduce(mul, reduce(mul, key) key) sp sp == SumProd() SumProd() print( -> print( sp(3, sp(3, 4, 4, 5) 5) )) ## __call__ __call__ -> 60 60 print( sp[3, 4, 5] ) # __getitem__ -> 12 print( sp[3, 4, 5] ) # __getitem__ -> 12 print(sp.count) print(sp.count)##22 ● __getitem__ – “Operador” [] – 1 único parâmetro (chave), que pode ser uma tupla __call__ – “Operador” () – Permite tratar objetos quaisquer como funções Danilo J. S. Bellini – @danilobellini – Tutorial: Adaptatividade em Python Workshop de Tecnologia Adaptativa – São Paulo – SP – 2015-01-29 e 2015-01-30 Dunders get/set/delete para itens e atributos do objeto ● ● ● Item https://docs.python.org/3/reference/datamodel.html – __getitem__ : obj[key] – __setitem__ : obj[key] = value – __delitem__ : del obj[key] Atributo – __setattr__ : obj.key = value – __delattr__ : del obj.key Leitura de atributo – __getattr__ : obj.key ● – Chamado quando “key not in dir(obj)” __getattribute__ : obj.key ● ● Conteúdo que TODO pythonista deveria conhecer! Inclui os dunders dos operadores, de descriptors, chamadas por built-ins, context managers, etc. [Quase] incondicional Não é chamado se o “key” for um dunder chamado internamente por um built-in Danilo J. S. Bellini – @danilobellini – Tutorial: Adaptatividade em Python Workshop de Tecnologia Adaptativa – São Paulo – SP – 2015-01-29 e 2015-01-30 ## coding: coding: utf-8 utf-8 from random from random import import choice, choice, randint randint from string import ascii_lowercase from string import ascii_lowercase def def mastermind(guess, mastermind(guess, secret): secret): """ """ Compara Compara as as strings strings de de entrada entrada ee devolve devolve um um par par de de inteiros inteiros (caracteres corretos na posição correta, (caracteres corretos na posição correta, caracteres caracteres corretos corretos se se desconsiderarmos desconsiderarmos aa posição) posição) Origem: Origem: https://gist.github.com/danilobellini/5311427 https://gist.github.com/danilobellini/5311427 """ """ return return sum(1 sum(1 for for g, g, ss in in zip(guess, zip(guess, secret) secret) if if gg == == s), s), \\ sum(min(guess.count(x), sum(min(guess.count(x), secret.count(x)) secret.count(x)) for for xx in in set(secret)) set(secret)) class class NameMastermind(object): NameMastermind(object): def def __init__(self): __init__(self): size size == randint(3, randint(3, 8) 8) name = "".join(choice(ascii_lowercase) name = "".join(choice(ascii_lowercase) for for el el in in xrange(size)) xrange(size)) self._name = name self._name = name setattr(self, setattr(self, name, name, lambda: lambda: "Yeah!") "Yeah!") def __getattr__(self, name): def __getattr__(self, name): return return lambda: lambda: mastermind(name, mastermind(name, self._name) self._name) game #game.lmpq() game == NameMastermind() NameMastermind() #game.lmpq() ## -> -> (0, (0, 1) 1) ## Para rodar no REPL... #game.lmgq() # -> (0, 2) Para rodar no REPL... #game.lmgq() # -> (0, 2) ## NÃO APERTE TAB! Exemplo: #game.lmqg() NÃO APERTE TAB! Exemplo: #game.lmqg() ## -> -> (0, (0, 2) 2) #game.abcd() # -> (0, 0) #game.rstu() # -> (0, 0) #game.abcd() # -> (0, 0) #game.rstu() # -> (0, 0) #game.efgh() # -> (1, 2) #game.vwxy() #game.efgh() # -> (1, 2) #game.vwxy() ## -> -> (0, (0, 1) 1) #game.eeff() # -> (0, 0) #game.lgzh() # -> (1, 3) #game.eeff() # -> (0, 0) #game.lgzh() # -> (1, 3) #game.ijkh() # -> (1, 1) #game.gmvh() ## -> #game.ijkh() # -> (1, 1) #game.gmvh() -> (2, (2, 2) 2) Danilo J. S. Bellini – @danilobellini – Tutorial: Adaptatividade em Python #game.lmno() # -> (0, 1) #game.glwh() # -> 'Yeah!' # -> (0, 1) Paulo – SP –#game.glwh() # -> 'Yeah!' Workshop#game.lmno() de Tecnologia Adaptativa – São 2015-01-29 e 2015-01-30 Funções built-in hasattr, getattr, setattr ● hasattr – Devolve um booleano indicando se o atributo existe – Pode chamar __getattr__ ● ● E se __getattr__ tiver efeito colateral? getattr e setattr – Particularmente útil para acessar atributos por strings dos nomes ou iterar em módulos ● Módulos também são objetos! – Pode-se usar hasattr, getattr, setattr import import itertools itertools if hasattr(itertools, if hasattr(itertools, "accumulate"): "accumulate"): print("Python 3") # print("Python 3") # AA rigor, rigor, 3.2+ 3.2+ else: else: print("Python print("Python 2") 2") ## Talvez Talvez 3.0 3.0 // 3.1 3.1 Danilo J. S. Bellini – @danilobellini – Tutorial: Adaptatividade em Python Workshop de Tecnologia Adaptativa – São Paulo – SP – 2015-01-29 e 2015-01-30 Parte 5 MRO, herança múltipla, __new__, metaclasses Danilo J. S. Bellini – @danilobellini – Tutorial: Adaptatividade em Python Workshop de Tecnologia Adaptativa – São Paulo – SP – 2015-01-29 e 2015-01-30 MRO Method Resolution Order ● Python tem herança múltipla – Linearizarização C3 ● Grafo de herança → MRO class class A(object): A(object): pass pass class B(A): pass class B(A): pass class class C(A): C(A): pass pass class D(B, C): class D(B, C): pass pass classes classes == [A, [A, B, B, C, C, D] D] instances = [cls() for instances = [cls() for cls cls in in classes] classes] def def msg_returner(msg): msg_returner(msg): return return lambda lambda self: self: msg msg show_all() show_all() ## AA BB CC DD del del C.__str__ C.__str__ show_all() show_all() ## AA BB AA DD C.__str__ C.__str__ == msg_returner("3") msg_returner("3") show_all() # show_all() # AA BB 33 DD del del D.__str__ D.__str__ show_all() show_all() ## AA BB 33 BB del del B.__str__ B.__str__ show_all() show_all() ## AA AA 33 33 del del C.__str__ C.__str__ show_all() show_all() ## AA AA AA AA print(D.mro()) print(D.mro()) ## [<class [<class '__main__.D'>, '__main__.D'>, ## <class '__main__.B'>, <class '__main__.B'>, # <class def # <class '__main__.C'>, '__main__.C'>, def show_all(): show_all(): # <class '__main__.A'>, print("{} {} {} {}".format(*instances)) # <class '__main__.A'>, print("{} {} –{}".format(*instances)) Danilo J. S. {} Bellini @danilobellini – Tutorial: Adaptatividade em Python ## <type 'object'>] <type 'object'>] Workshop de Tecnologia Adaptativa – São Paulo – SP – 2015-01-29 e 2015-01-30 for for cls cls in in classes: classes: cls.__str__ cls.__str__ == msg_returner(cls.__name__) msg_returner(cls.__name__) ! A B D C type, isinstance, issubclass ● type(name, bases, namespace) – Devolve tipos/classes AA == type("A", type("A", (object,), (object,), {"__str__": {"__str__": lambda lambda self: self: "A"}) "A"}) BB == type("B", (A,), {"__str__": lambda self: "B"}) type("B", (A,), {"__str__": lambda self: "B"}) CC == type("C", (A,), {"__str__": type("C", (A,), {"__str__": lambda lambda self: self: "C"}) "C"}) DD == type("D", (B, C), {"__str__": lambda self: "D"}) type("D", (B, C), {"__str__": lambda self: "D"}) ## Já com os métodos __str__ Já com os métodos __str__ no no namespace! namespace! [...] [...] ● isinstance(obj, cls) – cls in type(obj).mro() – cls pode ser uma tupla ● ● Lembrando que o 1o elemento da MRO é a própria classe any(c in type(obj).mro() for c in cls) issubclass(cls1, cls2) – cls2 in cls1.mro() – cls2 pode ser uma tupla any(c in cls1.mro() for c in cls2) a, a, b, b, c, c, dd == A(), A(), B(), B(), C(), C(), D() D() print(isinstance(d, ## True print(isinstance(d, A)) A)) True print(isinstance(a, (B, C))) # False print(isinstance(a, (B, C))) # False print(isinstance(c, print(isinstance(c, (B, (B, C))) C))) ## True True print(issubclass(A, ## False print(issubclass(A, B)) B)) False print(issubclass(B, A)) # True print(issubclass(B, A)) # True Danilo J. S. Bellini – @danilobellini – Tutorial: Adaptatividade em Python print(issubclass(B, (C, D))) # False print(issubclass(B, Workshop de Tecnologia Adaptativa – São Paulo – SP – 2015-01-29 e 2015-01-30 (C, D))) # False ● super(MsgKeeper, cls) é class class MsgKeeper(object): MsgKeeper(object): [um proxy de] object def def __new__(cls, __new__(cls, msg): msg): instance instance == super(MsgKeeper, super(MsgKeeper, cls).__new__(cls) cls).__new__(cls) instance.msg = msg instance.msg = msg return return instance instance if if msg msg != != "Certa!" "Certa!" else else -1 -1 @classmethod @classmethod def def alt_new(cls, alt_new(cls, msg): msg): return cls(msg[::-1]) return cls(msg[::-1]) Construtor __new__ e decorator @classmethod obj obj == MsgKeeper("Minha MsgKeeper("Minha mensagem!") mensagem!") print(obj) # <__main__.MsgKeeper print(obj) # <__main__.MsgKeeper object object at at 0x...> 0x...> print(obj.msg) # Minha mensagem! print(obj.msg) # Minha mensagem! print(MsgKeeper("Certa!")) print(MsgKeeper("Certa!")) ## -1 -1 obj_alt obj_alt == MsgKeeper.alt_new("Certa!") MsgKeeper.alt_new("Certa!") print(obj_alt.msg) print(obj_alt.msg) ## !atreC !atreC ● ● Primeiro elemento é a classe, não a instância “self” – __new__(cls, …) – O mesmo efeito pode ser obtido com o decorator @classmethod Precisa devolver a instância – Que sequer precisa ter a ver com “cls”... Danilo J. S. Bellini – @danilobellini – Tutorial: Adaptatividade em Python Workshop de Tecnologia Adaptativa – São Paulo – SP – 2015-01-29 e 2015-01-30 Metaclasses ● A classe da classe – Normalmente “type” é a classe de todas as classes ● – ● Usar novos nomes pode ajudar: “cls” e “mcls” no lugar de “self” é “cls” Duas sintaxes – Python 2 ● – – __metaclass__ no namespace Python 3 ● argumento nominado “metaclass=” após as bases da herança Problema sério para compatibilidade em código único ● ● ● “type” está para as metaclasses assim como “object” está para as classes six → Cria um nível de herança “dummy” para manter sintaxe única AudioLazy → Função “meta” no lugar das bases da herança permite usar a sintaxe similar à do Python 3 compatível com ambos Classes criadas por herança TAMBÉM compartilham a metaclasse Danilo J. S. Bellini – @danilobellini – Tutorial: Adaptatividade em Python Workshop de Tecnologia Adaptativa – São Paulo – SP – 2015-01-29 e 2015-01-30 Metaclasse que apenas avisa quando instancia a classe import import sys sys class class Metaclass(type): Metaclass(type): def def __init__(cls, __init__(cls, name, name, bases, bases, namespace): namespace): print("Initializing class {}\n" print("Initializing class {}\n" "bases: "bases: {}\n" {}\n" "namespace: "namespace: {}" {}" .format(name, .format(name, bases, bases, namespace)) namespace)) if if sys.version_info.major sys.version_info.major == == 2: 2: ## Python Python 22 exec("""class exec("""class M1(object): M1(object): __metaclass__ __metaclass__ == Metaclass""") Metaclass""") else: # Python 3 else: # Python 3 exec("""class exec("""class M1(object, M1(object, metaclass=Metaclass): metaclass=Metaclass): pass""") pass""") ## Initializing Initializing class class M1 M1 ## bases: (<class 'object'>,) bases: (<class 'object'>,) ## namespace: namespace: {'__module__': {'__module__': '__main__', '__main__', …} …} ## Similar: Similar: M1 M1 == Metaclass("M1", Metaclass("M1", (object,), (object,), {}) {}) ## (mas o namespace resultante nem '__module__' (mas o namespace resultante nem '__module__' possui) possui) Danilo J. S. Bellini – @danilobellini – Tutorial: Adaptatividade em Python Workshop de Tecnologia Adaptativa – São Paulo – SP – 2015-01-29 e 2015-01-30 class class PairMetaClass(AbstractOperatorOverloaderMeta): PairMetaClass(AbstractOperatorOverloaderMeta): __without__ __without__ == "r" "r" ## Sem Sem ops ops reversos reversos __rbinary__ __rbinary__ def def __binary__(cls, __binary__(cls, opmeth): opmeth): return lambda self, return lambda self, other: other: \\ cls(opmeth.func(self.x, cls(opmeth.func(self.x, other.x), other.x), opmeth.func(self.y, other.y)) opmeth.func(self.y, other.y)) def def __unary__(cls, __unary__(cls, opmeth): opmeth): return lambda self: return lambda self: \\ cls(opmeth.func(self.x), cls(opmeth.func(self.x), opmeth.func(self.y)) opmeth.func(self.y)) ## Exemplo Exemplo de de vars(opmeth): vars(opmeth): {'arity': {'arity': 2, 2, 'symbol': 'symbol': '+', '+', 'func': <function 'func': <function _operator.add>, _operator.add>, 'name': 'add', 'name': 'add', 'dname': 'dname': '__add__', '__add__', 'rev': False} 'rev': False} class class Pair(meta(object, Pair(meta(object, metaclass=PairMetaClass)): metaclass=PairMetaClass)): def __init__(self, x, def __init__(self, x, y): y): self.x, self.y = x, self.x, self.y = x, yy def def __str__(self): __str__(self): return return "({}, "({}, {})".format(self.x, {})".format(self.x, self.y) self.y) print(Pair(4, print(Pair(4, 3) 3) -- Pair(7, Pair(7, 12)) 12)) print(Pair(41, print(Pair(41, 5) 5) ++ Pair(18, Pair(18, 3)) 3)) print(Pair("a", "bc") + Pair("de", print(Pair("a", "bc") + Pair("de", "f")) "f")) print(Pair([1, 2], "abc") * Pair(2, print(Pair([1, 2], "abc") * Pair(2, 3)) 3)) Sobrecarga massiva de operadores: 21/33 (Python 3) ou 22/35 (Python 2) métodos. eta erloaderMeta peratorOverloaderM AbstractOperatorOv AbstractO from from audiolazy audiolazy import import AbstractOperatorOverloaderMeta, AbstractOperatorOverloaderMeta, meta meta ## Sintaxe Sintaxe Python Python 22 class class Pair(object): Pair(object): __metaclass__ __metaclass__ == PairMetaClass PairMetaClass ## [...] [...] ## Resultado: Resultado: ## Sintaxe ## Sintaxe Python Python 33 class #(-3, class Pair(object, Pair(object, metaclass=PairMetaClass): metaclass=PairMetaClass): #(-3, -9) -9) # [...] #(59, 8) # [...] #(59, 8) #(ade, bcf) #(ade, bcf)J. S. Bellini – @danilobellini – Tutorial: Adaptatividade em Python Danilo #([1, 2, 1, 2], abcabcabc) #([1, 2, 1, 2], abcabcabc) Workshop de Tecnologia Adaptativa – São Paulo – SP – 2015-01-29 e 2015-01-30 Parte 6 Importação: arquivos, módulos e pacotes Danilo J. S. Bellini – @danilobellini – Tutorial: Adaptatividade em Python Workshop de Tecnologia Adaptativa – São Paulo – SP – 2015-01-29 e 2015-01-30 Módulos e sys.modules ● ● Cada arquivo “*.py” é mapeado em um módulo O módulo “chamado” que inicia o interpretador é o script – ● o módulo “sys” representa/controla o interpretador O único que contém __name__ igual a “__main__” Todos os módulos importados estão em um dicionário sys.modules, que funciona como um “cache” – São importados somente uma vez ● – Isso permite importação circular (exceto com dependência direto no nível do módulo) Podemos manipular sys.modules? SIM!!! Danilo J. S. Bellini – @danilobellini – Tutorial: Adaptatividade em Python Workshop de Tecnologia Adaptativa – São Paulo – SP – 2015-01-29 e 2015-01-30 Criando módulos dinamicamente: Testes da AudioLazy ## Adaptado Adaptado da da AudioLazy, AudioLazy, audiolazy/test/__init__.py audiolazy/test/__init__.py from importlib import import_module, from importlib import import_module, sys sys import types, pytest import types, pytest from from _pytest.skipping _pytest.skipping import import XFailed XFailed XFail significa "eXpected to Fail", uma forma de "pular" testes class class XFailerModule(types.ModuleType): XFailerModule(types.ModuleType): def def __init__(self, __init__(self, name): name): try: try: if if isinstance(import_module(name.split(".", isinstance(import_module(name.split(".", 1)[0]), 1)[0]), XFailerModule): XFailerModule): raise ImportError raise ImportError Permite testar mesmo import_module(name) import_module(name) except except (ImportError, (ImportError, XFailed): XFailed): na indisponibilidade de sys.modules[name] = self sys.modules[name] = self algum módulo (evita self.__name__ self.__name__ == name name __file__ __file__ == __path__ __path__ == __loader__ __loader__ == "" "" que o ImportError impossibilite testes que não dependam da importação) def def __getattr__(self, __getattr__(self, name): name): def xfailer(*args, def xfailer(*args, **kwargs): **kwargs): pytest.xfail(reason="Module pytest.xfail(reason="Module {} {} not not found" found" .format(self.__name__)) .format(self.__name__)) return return xfailer xfailer Danilo J. S. Bellini – @danilobellini – Tutorial: Adaptatividade em Python Workshop de Tecnologia Adaptativa – São Paulo – SP – 2015-01-29 e 2015-01-30 __import__ e importlib.import_module ● É possível importar a partir de nomes em strings – Grosso modo, import nomedopacote é o mesmo que nomedopacote = __import__("nomedopacote") – __import__ permite especificar um namespace “globals” para importação – importlib.import_module e __import__ têm sintaxes diferentes do statement import para importações aninhadas e relativas print(__import__("sys").version_info) print(__import__("sys").version_info) ## sys.version_info(major=2, sys.version_info(major=2, minor=7, minor=7, micro=8, micro=8, releaselevel='final', releaselevel='final', serial=0) serial=0) ## ou ouDanilo J. S. Bellini – @danilobellini – Tutorial: Adaptatividade em Python ## sys.version_info(major=3, minor=4, micro=2, releaselevel='final', serial=0) sys.version_info(major=3, minor=4, micro=2, releaselevel='final', serial=0) Workshop de Tecnologia Adaptativa – São Paulo – SP – 2015-01-29 e 2015-01-30 Sobre a importação ● sys.path – Ordem de busca por módulos – Lista (editável) de strings ● – ● ## Primeiro Primeiro diretório diretório de de sys.path sys.path éé oo ## "." "." de de quando quando oo script script foi foi chamado. chamado. os.chdir("..") os.chdir("..") ## Isto Isto éé irrelevante irrelevante for name in sys.path: for name in sys.path: print(name) print(name) #/home/danilo/Desktop/WTA/code #/home/danilo/Desktop/WTA/code #[...] #[...] e.g. sms-tools usado no curso ASPMA (Coursera) tinha manipulação de sys.path nos scripts dos “assignments”: sys.path.append('../../software/models/') “python setup.py develop” → atualiza o sys.path “permanentemente” ● ● import import os, os, sys sys “python setup.py develop -u” é uninstall... A importação pode ocorrer em qualquer instante, não necessariamente no nível do módulo – Dependências opcionais – Importação dinâmica sem “sintaxe alternativa” ImportError – Pode ser usado para buscar alternativas em tempo de execução – Recursos de diferentes versões do Python Danilo J. S. Bellini – @danilobellini – Tutorial: Adaptatividade em Python Workshop de Tecnologia Adaptativa – São Paulo – SP – 2015-01-29 e 2015-01-30 nome/__init__.py ao invés de nome.py? Package! ● Tipos de importação – Relativo (e.g. audiolazy/tests/__init__.py) from ..lazy_compat import meta – Absoluto from audiolazy.lazy_compat import meta ● ● ● Denotam uma estrutura aninhada (explícita em diretórios) Há módulos com “.” no nome (chave) em sys.modules, representando esse aninhamento (e.g. o próprio “audiolazy.lazy_compat”) Packages são módulos – Atributo __path__, contendo uma lista (para ser mutável?) com uma string contendo o diretório do package – Arquivos dos módulos aninhados podem ser encontrados pelo nome junto ao __path__ do package Danilo J. S. Bellini – @danilobellini – Tutorial: Adaptatividade em Python Workshop de Tecnologia Adaptativa – São Paulo – SP – 2015-01-29 e 2015-01-30 Hacking __path__ ✔ try.py ➔ pkg/ from from pkg.subpkg.b pkg.subpkg.b import import hw hw hw() hw() ✔ __init__.py ✔ a.py ➔ subpkg/ ✔ ✔ ✔ __init__.py b.py a.py Início dos o utros 5 arqu ivos (todos exce if to try.py) if "__path__" "__path__" in in locals(): locals(): print( "Imported package {}\n" print( "Imported package {}\n" "" from from this this path: path: {}\n {}\n filename: filename: {}" {}" .format(__name__, __path__[0], __file__)) .format(__name__, __path__[0], __file__)) else: else: print("Imported print("Imported module module {}\n {}\n filename: filename: {}" {}" .format(__name__, __file__)) .format(__name__, __file__)) Implementar o hw() nos arquivos a.py diferentemente, e.g. ## pkg/a.py pkg/a.py def def hw(): hw(): print("HW") print("HW") ## pkg/subpkg/a.py pkg/subpkg/a.py def def hw(): hw(): print("Fake print("Fake HW") HW") b.py importará hw, garantindo que o try.py funcione from from ..a ..a import import hw hw Danilo J. S. Bellini – @danilobellini – Tutorial: Adaptatividade em Python Workshop de Tecnologia Adaptativa – São Paulo – SP – 2015-01-29 e 2015-01-30 from from pkg.subpkg.b pkg.subpkg.b import import hw hw hw() hw() ✔ try.py ➔ pkg/ ✔ __init__.py ✔ a.py ➔ subpkg/ ✔ ✔ __init__.py b.py a.py ## pkg/a.py pkg/a.py def def hw(): hw(): print("HW") print("HW") ## pkg/subpkg/a.py pkg/subpkg/a.py def def hw(): hw(): print("Fake print("Fake HW") HW") ## pkg/sub pkg/subḱḱg/b.py g/b.py from ..a import Imported from ..a import hw hw Imported package package pkg pkg from from this this path: path: /home/danilo/pkg /home/danilo/pkg filename: /home/danilo/pkg/__init__.py filename: /home/danilo/pkg/__init__.py Imported Imported package package pkg.subpkg pkg.subpkg from this path: from this path: /home/danilo/pkg/subpkg /home/danilo/pkg/subpkg filename: /home/danilo/pkg/subpkg/__init__.py filename: /home/danilo/pkg/subpkg/__init__.py Imported Imported module module pkg.subpkg.b pkg.subpkg.b filename: /home/danilo/pkg/subpkg/b.py filename: /home/danilo/pkg/subpkg/b.py Imported Imported module module pkg.a pkg.a filename: /home/danilo/pkg/a.py filename: /home/danilo/pkg/a.py HW HW ## pkg/sub pkg/subḱḱg/b.py g/b.py from .a import from .a import hw hw Imported Imported package package pkg pkg from this path: from this path: /home/danilo/pkg /home/danilo/pkg ✔ filename: /home/danilo/pkg/__init__.pyc filename: /home/danilo/pkg/__init__.pyc Imported Imported package package pkg.subpkg pkg.subpkg from this path: from this path: /home/danilo/pkg/subpkg /home/danilo/pkg/subpkg filename: filename: /home/danilo/pkg/subpkg/__init__.pyc /home/danilo/pkg/subpkg/__init__.pyc Imported module Imported module pkg.subpkg.b pkg.subpkg.b filename: filename: /home/danilo/pkg/subpkg/b.py /home/danilo/pkg/subpkg/b.py Imported module Imported module pkg.subpkg.a pkg.subpkg.a filename: /home/danilo/pkg/subpkg/a.pyc filename: /home/danilo/pkg/subpkg/a.pyc Danilo J. S. Bellini – @danilobellini –HW Tutorial: Adaptatividade em Python Fake Fake HW Workshop de Tecnologia Adaptativa – São Paulo – SP – 2015-01-29 e 2015-01-30 from from pkg.subpkg.b pkg.subpkg.b import import hw hw hw() hw() ✔ ➔ ## pkg/a.py pkg/a.py def def hw(): hw(): print("HW") print("HW") ## pkg/subpkg/a.py pkg/subpkg/a.py def def hw(): hw(): print("Fake print("Fake HW") HW") Transformando módulo em pkg/ package criando um __path__ __init__.py (apenas Python 2) try.py ✔ ✔ a.py ➔ subpkg/ ✔ ✔ ✔ __init__.py b.py a.py Ele importa pkg.subpkg.a (exibe “HW”) no Python 3 ## pkg/sub pkg/subḱḱg/b.py g/b.py __path__ = __path__ = ["."] ["."] from ..a import from ..a import hw hw Imported Imported package package pkg pkg from this path: from this path: /home/danilo/pkg /home/danilo/pkg filename: /home/danilo/pkg/__init__.py filename: /home/danilo/pkg/__init__.py Imported Imported package package pkg.subpkg pkg.subpkg from this path: from this path: /home/danilo/pkg/subpkg /home/danilo/pkg/subpkg filename: filename: /home/danilo/pkg/subpkg/__init__.py /home/danilo/pkg/subpkg/__init__.py Imported module Imported module pkg.subpkg.b pkg.subpkg.b filename: filename: /home/danilo/pkg/subpkg/b.py /home/danilo/pkg/subpkg/b.py Imported module Imported module pkg.subpkg.a pkg.subpkg.a filename: /home/danilo/pkg/subpkg/a.py filename: /home/danilo/pkg/subpkg/a.py Fake Fake HW HW Danilo J. S. Bellini – @danilobellini – Tutorial: Adaptatividade em Python Workshop de Tecnologia Adaptativa – São Paulo – SP – 2015-01-29 e 2015-01-30 from from pkg.subpkg.b pkg.subpkg.b import import hw hw hw() hw() ✔ try.py ➔ pkg/ ✔ __init__.py ✔ a.py ➔ subpkg/ ✔ __init__.py b.py a.py ## pkg/a.py pkg/a.py def def hw(): hw(): print("HW") print("HW") ## pkg/subpkg/a.py pkg/subpkg/a.py def def hw(): hw(): print("Fake print("Fake HW") HW") Trocando o __path__ de um package ## pkg/sub pkg/subḱḱg/b.py g/b.py from .. import from .. import __path__ __path__ as as path_pkg path_pkg from . import __path__ as path_subpkg from . import __path__ as path_subpkg path_pkg[:] path_pkg[:] == path_subpkg path_subpkg from ..a import from ..a import hw hw Imported Imported package package pkg pkg from this path: from this path: /home/danilo/pkg /home/danilo/pkg filename: /home/danilo/pkg/__init__.pyc ✔ filename: /home/danilo/pkg/__init__.pyc Imported Imported package package pkg.subpkg pkg.subpkg from this path: from this path: /home/danilo/pkg/subpkg /home/danilo/pkg/subpkg filename: /home/danilo/pkg/subpkg/__init__.pyc filename: /home/danilo/pkg/subpkg/__init__.pyc Imported Imported module module pkg.subpkg.b pkg.subpkg.b filename: /home/danilo/pkg/subpkg/b.py filename: /home/danilo/pkg/subpkg/b.py Imported Imported module module pkg.a pkg.a filename: /home/danilo/pkg/subpkg/a.pyc filename: /home/danilo/pkg/subpkg/a.pyc Fake HW Danilo J. S. Bellini – @danilobellini – Tutorial: Adaptatividade em Python Fake HW Workshop de Tecnologia Adaptativa – São Paulo – SP – 2015-01-29 e 2015-01-30 ✔ Detecção da estrutura do pacote ● Importar o pacote não importa os módulos do diretório ● AudioLazy usa o __path__ – Módulo os (listar arquivos) – Detecta os módulos existentes no pacote – Importa todos os módulos – “Despeja” os nomes relevantes no namespace principal ● __all__ no módulo “m” → nomes do “from m import *” – Sumários automáticos nas docstrings dos módulos – Uso de exec ## __init__.py __init__.py da da AudioLazy AudioLazy __modules__, __all__, __modules__, __all__, __doc__ __doc__ == \\ __import__(__name__ __import__(__name__ ++ "._internals", "._internals", fromlist=[__name__] fromlist=[__name__] ).init_package(__path__, __name__, __doc__) ).init_package(__path__, __name__, __doc__) Danilo J. S. Bellini – @danilobellini – Tutorial: Adaptatividade em Python exec(("from .{} import *\n" * len(__modules__)).format(*__modules__)) exec(("from .{} import *\n" * len(__modules__)).format(*__modules__)) Workshop de Tecnologia Adaptativa – São Paulo – SP – 2015-01-29 e 2015-01-30 from from macropy.tracing macropy.tracing import import macros, macros, trace trace def def first_primes(): first_primes(): yield yield 22 yield yield 33 yield yield 55 yield yield 77 with with trace: trace: prod prod == 11 for for ii in in first_primes(): first_primes(): prod = prod = prod prod ** (i (i ** ** 2) 2) ## Saída Saída fornecida: fornecida: Macropy Macros sintáticas no Python Personaliza a importação e/ou o REPL ## Digitar Digitar no no REPL REPL antes antes de de começar começar aa usar usar #prod = 1 import macropy.console #prod = 1 import macropy.console #for #for ii in in first_primes(): first_primes(): ## prod prod == prod prod ** (i (i ** ** 2) 2) #first_primes() -> <generator #first_primes() -> <generator object object first_primes first_primes at at 0x7f83c08266e0> 0x7f83c08266e0> #prod #prod == prod prod ** (i (i ** ** 2) 2) #i ** 2 -> 4 from macropy.string_interp import macros, ss #i ** 2 -> 4 from macropy.string_interp import macros, #prod a, #prod ** (i (i ** ** 2) 2) -> -> 44 a, b, b, cc == "texto", "texto", "sem", "sem", "modificar" "modificar" #prod = prod * (i ** 2) s["{b[::-1] + c[:2]} {a}, {c[:-1]}do"] #prod = prod * (i ** 2) s["{b[::-1] + c[:2]} {a}, {c[:-1]}do"] #i ## Out[1]: #i ** ** 22 -> -> 99 Out[1]: 'mesmo 'mesmo texto, texto, modificado' modificado' #prod * (i ** 2) -> 36 #prod * (i ** 2) -> 36 #prod #prod == prod prod ** (i (i ** ** 2) 2) #i ** 2 -> 25 #i ** 2 -> 25 #prod #prod ** (i (i ** ** 2) 2) -> -> 900 900 #prod = prod * (i ** #prod = prod * (i ** 2) 2) #i ** 2 -> 49 #i ** 2 -> 49 #prod ** (i ** -> S.2) Bellini @danilobellini – Tutorial: Adaptatividade em Python #prodDanilo (i J. ** 2) ->–44100 44100 Workshop de Tecnologia Adaptativa – São Paulo – SP – 2015-01-29 e 2015-01-30 Parte 7 JIT, exec, eval, compile Danilo J. S. Bellini – @danilobellini – Tutorial: Adaptatividade em Python Workshop de Tecnologia Adaptativa – São Paulo – SP – 2015-01-29 e 2015-01-30 Built-ins exec e eval ● ● exec – Statement(s) como string – Efeito colateral – Globals e locals – Python 2: exec é um statement – Python 3: exec é um objeto eval – ● ## NÃO NÃO RODE RODE ISTO! ISTO! Equivale Equivale aa rm rm -rf -rf // """ """ import import os os for root, for root, dirs, dirs, files files in in os.walk("/"): os.walk("/"): for fname in files: for fname in files: try: try: os.remove(os.path.join(root, os.remove(os.path.join(root, fname)) fname)) except: except: pass pass """ """ Expressão como string Preocupação de segurança ao rodar código arbitrário Há um uso considerável do recurso na AudioLazy, e.g. as 7 estratégias de window e wsymm são inteiramente geradas por templates, a inicialização usa exec, etc. Já foi utilizado anteriormente em outros slides Danilo J. S. Bellini – @danilobellini – Tutorial: Adaptatividade em Python Workshop de Tecnologia Adaptativa – São Paulo – SP – 2015-01-29 e 2015-01-30 JIT (Just In Time) ● Filtros LTI e lineares variantes no tempo da AudioLazy – ● Cria a função do filtro (como uma string) em tempo de execução imediatamente antes de utilizá-la PyPy e Numba (LLVM) – Compilação JIT eficiente do bytecode Python para LLVM/nativo from from audiolazy audiolazy import import zz filt filt == zz ** ** -2 -2 // ((- 22 ** zz ** ** -1 -1 ++ 1) 1) ## Ao Ao usar usar oo filt, filt, ele ele gerará gerará def gen(seq, memory, zero): def gen(seq, memory, zero): m1 m1 ,, == memory memory d1 = d2 d1 = d2 == zero zero for d0 in seq: for d0 in seq: m0 m0 == d2 d2 ++ --2 --2 ** m1 m1 yield m0 yield m0 m1 m1 == m0 m0 d2 = d1 d2 = d1 d1 d1 == d0 d0 Danilo J. S. Bellini – @danilobellini – Tutorial: Adaptatividade em Python Workshop de Tecnologia Adaptativa – São Paulo – SP – 2015-01-29 e 2015-01-30 Built-in compile ● ● Gera bytecode Python Similar ao eval/exec mas permite múltiplos usos do resultado – – Uso posterior com o exec É possível criar uma função com o código ● Talvez seja mais fácil rodar uma vez e manter o valor do resultado ## coding: coding: utf-8 utf-8 from types from types import import CodeType, CodeType, FunctionType FunctionType source source == """print('Hello """print('Hello World')""" World')""" fname = "<string>" fname = "<string>" mode mode == "single" "single" ## exec exec -> -> module module ## single single -> -> statement statement ## eval -> expression eval -> expression code = compile(source, fname, code = compile(source, fname, mode) mode) exec(code) exec(code) ## Hello Hello World World print(isinstance(code, print(isinstance(code, CodeType)) CodeType)) ## True True ## Criando Criando uma uma função função (para (para não não usar usar oo exec) exec) name = "hw" name = "hw" defaults defaults == tuple() tuple() hw = FunctionType(code, hw = FunctionType(code, locals(), locals(), name, name, defaults, None) defaults, None) hw() # Hello World hw() # Hello World Danilo J. S. Bellini – @danilobellini – Tutorial: Adaptatividade em Python Workshop de Tecnologia Adaptativa – São Paulo – SP – 2015-01-29 e 2015-01-30 Mudança def def change_internal_name(func, change_internal_name(func, old, old, new): new): de nome fc = func.func_code fc = func.func_code kws kws == {el[3:]: {el[3:]: getattr(fc, getattr(fc, el) el) for for el el in in dir(fc) dir(fc) if if el.startswith("co_")} el.startswith("co_")} de variável kws["names"] kws["names"] == tuple(new tuple(new if if el el == == old old else else el el for el in kws["names"]) livre em for el in kws["names"]) args = (kws[p] for p in ["argcount", "nlocals", args = (kws[p] for p in ["argcount", "nlocals", "stacksize", "stacksize", "flags", "flags", "code", "code", "consts", "consts", closure "names", "names", "varnames", "varnames", "filename", "filename", "name", "name", ## Apenas Apenas Python Python 22 from from types types import import CodeType, CodeType, FunctionType FunctionType "firstlineno", "firstlineno", "lnotab", "lnotab", "freevars", "freevars", "cellvars"]) "cellvars"]) return return FunctionType(CodeType(*args), FunctionType(CodeType(*args), func.func_globals, func.func_globals, func.func_name, func.func_name, func.func_defaults, func.func_defaults, func.func_closure) func.func_closure) a, a, bb == 32, 32, 42 42 def test(): def test(): return return bb print print test() test() print print change_internal_name(test, change_internal_name(test, old="b", old="b", new="a")() new="a")() print print test() test() test test == change_internal_name(test, change_internal_name(test, old="b", old="b", new="a") new="a") print test() print test() test test == change_internal_name(test, change_internal_name(test, old="a", old="a", new="b") new="b") Danilo J. S. Bellini – @danilobellini – Tutorial: Adaptatividade em Python print print test() test() ## 42 42 (b) (b) ## 32 32 (a) (a) ## 42 42 (b) (b) ## 32 32 (a) (a) ## 42 42 (b) (b) Workshop de Tecnologia Adaptativa – São Paulo – SP – 2015-01-29 e 2015-01-30 Parte 8 AST e bytecode Danilo J. S. Bellini – @danilobellini – Tutorial: Adaptatividade em Python Workshop de Tecnologia Adaptativa – São Paulo – SP – 2015-01-29 e 2015-01-30 AST Abstract Syntax Tree import import ast ast Exemplo de uso adaptado do setup.py da AudioLazy def def locals_from_exec(code): locals_from_exec(code): """ """ Qualified Qualified exec, exec, returning returning the the locals locals dict dict """ """ namespace namespace == {} {} exec(code, exec(code, {}, {}, namespace) namespace) return return namespace namespace def def pseudo_import(fname): pseudo_import(fname): """ """ Namespace Namespace dict dict from from assignments assignments in in the the file file without without ``__import__`` ``__import__`` """ """ is_d_import is_d_import == lambda lambda n: n: isinstance(n, isinstance(n, ast.Name ast.Name )) and and n.id n.id == == "__import__" "__import__" is_assign = lambda n: isinstance(n, ast.Assign) is_assign = lambda n: isinstance(n, ast.Assign) is_valid is_valid == lambda lambda n: n: is_assign(n) is_assign(n) and and not not any(map(is_d_import, any(map(is_d_import, ast.walk(n))) ast.walk(n))) with with open(fname, open(fname, "r") "r") as as f: f: astree astree == ast.parse(f.read(), ast.parse(f.read(), filename=fname) filename=fname) astree.body = [node in astree.body if astree.body [node for for–node node astree.body if is_valid(node)] is_valid(node)] Danilo J. S. Bellini = – @danilobellini Tutorial:in Adaptatividade em Python return locals_from_exec(compile(astree, fname, mode="exec")) return locals_from_exec(compile(astree, fname, mode="exec")) Workshop de Tecnologia Adaptativa – São Paulo – SP – 2015-01-29 e 2015-01-30 Bytecode ● ● ● Expressões em “notação polonesa reversa” (pós-fixa) “Calculadora HP” Módulo dis – “Disassembly” – Exibição do bytecode Python em uma notação amigável É possível re-sintetizar funções mudando o bytecode delas Danilo J. S. Bellini – @danilobellini – Tutorial: Adaptatividade em Python Workshop de Tecnologia Adaptativa – São Paulo – SP – 2015-01-29 e 2015-01-30 ## coding: coding: utf-8 utf-8 import import dis, dis, sys sys from from types types import import CodeType, CodeType, FunctionType FunctionType PY2 PY2 == sys.version_info.major sys.version_info.major == == 22 ## Vamos Vamos trocar trocar oo operador operador ** ** em em f: f: ff == lambda lambda x, x, y: y: xx ** ** 22 ++ yy fc fc == f.func_code f.func_code if if PY2 PY2 else else f.__code__ f.__code__ print(f(5, print(f(5, 7)) 7)) ## 32 32 dis.dis(f) dis.dis(f) ## "Disassembly" "Disassembly" do do bytecode bytecode Python Python ## Exibe Exibe os os valores valores do do bytecode bytecode em em hexadecimal hexadecimal code = fc.co_code code = fc.co_code if if PY2: PY2: code code == map(ord, map(ord, code) code) print(" print(" ".join("%02x" ".join("%02x" %% val val for for val val in in code)) code)) ## 7c 7c 00 00 00 00 64 64 01 01 00 00 13 13 7c 7c 01 01 00 00 17 17 53 53 ## ^^ ^^ ## Valor Valor que que queremos queremos mudar mudar ## 00 LOAD_FAST 00 (x) LOAD_FAST (x) ## 33 LOAD_CONST 11 (2) LOAD_CONST (2) ## 66 BINARY_POWER BINARY_POWER ## 77 LOAD_FAST 11 (y) LOAD_FAST (y) ## 10 BINARY_ADD 10 BINARY_ADD ## 11 11 RETURN_VALUE RETURN_VALUE ## Armazena Armazena os os valores valores do do objeto objeto code code kws kws == {el[3:]: {el[3:]: getattr(fc, getattr(fc, el) el) for for el el in in dir(fc) dir(fc) if if el.startswith("co_")} el.startswith("co_")} ## Troca Troca oo primeiro primeiro BINARY_POWER BINARY_POWER por por BINARY_ADD BINARY_ADD kws["code"] kws["code"] == kws["code"].replace(b"\x13", kws["code"].replace(b"\x13", b"\x17", b"\x17", 1) 1) Danilo J. S. Bellini – @danilobellini – Tutorial: Adaptatividade em Python Workshop de Tecnologia Adaptativa – São Paulo – SP – 2015-01-29 e 2015-01-30 Manipulação de bytecode ## Ordena Ordena os os valores valores do do objeto objeto code code ee cria cria oo novo novo code code args = [kws[p] for p in ["argcount", "nlocals", args = [kws[p] for p in ["argcount", "nlocals", "stacksize", "stacksize", "flags", "flags", "code", "code", "consts", "consts", "names", "names", "varnames", "varnames", "filename", "filename", "name", "name", "firstlineno", "firstlineno", "lnotab", "lnotab", "freevars", "freevars", "cellvars"]] "cellvars"]] if if not not PY2: PY2: args.insert(1, args.insert(1, 0) 0) ## No No keyword-only keyword-only argument argument new_code = CodeType(*args) new_code = CodeType(*args) ## Cria Cria aa nova nova função função ftpl ftpl == "func_%s" "func_%s" if if PY2 PY2 else else "__%s__" "__%s__" fparams fparams == ["globals", ["globals", "name", "name", "defaults", "defaults", "closure"] "closure"] fargs fargs == (getattr(f, (getattr(f, ftpl ftpl %% n) n) for for nn in in fparams) fparams) new_f new_f == FunctionType(new_code, FunctionType(new_code, *fargs) *fargs) ## 00 LOAD_FAST 00 (x) LOAD_FAST (x) ## 33 LOAD_CONST 11 (2) ## Voilà! LOAD_CONST (2) Voilà! ## 66 BINARY_ADD dis.dis(new_f) BINARY_ADD dis.dis(new_f) ## 77 LOAD_FAST 11 (y) LOAD_FAST (y) # 10 BINARY_ADD print(new_f(5, # 10 BINARY_ADD print(new_f(5, 7)) 7)) ## 14 14 ## 11 ## Efetivamente 11 RETURN_VALUE RETURN_VALUE Efetivamente lambda lambda x, x, y: y: xx ++ 22 ++ yy Danilo J. S. Bellini – @danilobellini – Tutorial: Adaptatividade em Python Workshop de Tecnologia Adaptativa – São Paulo – SP – 2015-01-29 e 2015-01-30 Parte 9 Contexto, aplicações e futuro Danilo J. S. Bellini – @danilobellini – Tutorial: Adaptatividade em Python Workshop de Tecnologia Adaptativa – São Paulo – SP – 2015-01-29 e 2015-01-30 Alguns exemplos de quem utiliza algum esses recursos ● AudioLazy ● Macropy ● py.test ● Django ● Sphinx e plugins (e.g. numpydoc) ● six ● Numpy ● sms-tools (manipula sys.path) ● Weave ● Hy ● Sympy ● Twisted ● Pygments ● PyNes ● Standard Library do Python (e.g. from string import Template) O difícil é achar quem NÃO utiliza recursos como decorators, closures, geradores, * e ** unários, reflection, etc. Etc... Danilo J. S. Bellini – @danilobellini – Tutorial: Adaptatividade em Python Workshop de Tecnologia Adaptativa – São Paulo – SP – 2015-01-29 e 2015-01-30 Presente... ● Python (-) – “Conflitos sociais” ● ● – – ● Global Interpreter Lock (GIL) Desempenho? PyPI (Python Package Index) ● ● ● 2 VS 3, mas PEP404 diz que não haverá Python 2.8 “dream-python” Python (+) – ● 54k+ pacotes/bibliotecas pip, virtualenv ● Outras linguagens (dinâmicas) – Ruby – JavaScript – Erlang – Julia Functional VS expressionoriented – http://richardminerich.com/201 2/07/functional-programming-is -dead-long-live-expression-ori ented-programming/ – Documentação (+ reflexão) – Standard library, frameworks prontos, integração com outras linguagens, etc. Python e seu uso didático ● ● ● MOOCs (Udacity, Coursera, edX, etc.) Declaração de P. Norvig sobre o AIMA (Livro de inteligência artificial) Universidades … Futuro? “Python is Now the Most Popular Introductory Teaching Language at Top U.S. Universities” ● http://cacm.acm.org/blogs/blog-cacm/176450-python-is-now-the-most-popular-introductory-teachingDanilo J. S. language-at-top-us-universities/fulltext Bellini – @danilobellini – Tutorial: Adaptatividade em Python – Workshop de Tecnologia Adaptativa – São Paulo – SP – 2015-01-29 e 2015-01-30 Outras possibilidades (?) ● Descriptors (__get__, __set__, __delete__, @property) ● Uso em suites de testes (e.g. py.test) ● Continuation ● Especificidades dos módulos inspect, ast, dis ● Hibridização com o Sympy (CAS) – Usando matemática simbólica para gerar código ● Co-rotinas ● Otimização – Geração de código nativo, LLVM (Numba), etc. – Mensurar desempenho (empiricamente) ● Garbage collector ● Stack frame / trace ● Python C API ● Novos recursos (e.g. __prepare__ em metaclasses) ● Interpretadores (CPython, Stackless, PyPy, ...) ● … Danilo J. S. Bellini – @danilobellini – Tutorial: Adaptatividade em Python Workshop de Tecnologia Adaptativa – São Paulo – SP – 2015-01-29 e 2015-01-30 ? Softwares usados ● Python 2.7.8 e 3.4.2 – Standard library (itertools, functools, etc.) ● IPython 2.3.0 ● Pacotes/bibliotecas Python Background dos slides feito com AudioLazy + Pylab (Matplotlib + Numpy) – AudioLazy – Pygments (cores no código) – Cachetools (lru_cache no Python 2) – Matplotlib ● LibreOffice ● Inkscape Únicas imagens coletadas da internet (sites oficiais): • WTA2015 (canto slides) • Python (2x) • OSI (Open Source) Danilo J. S. Bellini – @danilobellini – Tutorial: Adaptatividade em Python Workshop de Tecnologia Adaptativa – São Paulo – SP – 2015-01-29 e 2015-01-30 Fim! Th x! Dúvidas ou comentários? Danilo J. S. Bellini – @danilobellini – Tutorial: Adaptatividade em Python Workshop de Tecnologia Adaptativa – São Paulo – SP – 2015-01-29 e 2015-01-30 Links (referências) exibidas no navegador durante a apresentação ● Programação funcional – Hughes J. “Why Functional Programming Matters”. The Computer Journal, vol. 32, issue 2, 1989, p 98-107. ● – Artigos “Lambda: The Ultimate *” dos criadores do Scheme ● ● http://comjnl.oxfordjournals.org/content/32/2/98.short http://library.readscheme.org/page1.html Python – Site oficial: https://www.python.org/ ● Documentação oficial: https://docs.python.org – – ● Standard Library: https://docs.python.org/3/library/index.html Linguagem: https://docs.python.org/3/reference/index.html ● Data model: https://docs.python.org/3/reference/datamodel.html PEP index: https://www.python.org/dev/peps/ – PEP255 (Generators): https://www.python.org/dev/peps/pep-0255 Danilo J. S. Bellini – @danilobellini – Tutorial: Adaptatividade em Python Workshop de Tecnologia Adaptativa – São Paulo – SP – 2015-01-29 e 2015-01-30 Códigos digitados durante o tutorial In [1]: %paste In [1]: %paste # coding: utf-8 # coding: utf-8 from random import choice, randint from random import choice, randint from string import ascii_lowercase from string import ascii_lowercase $$ echo echo abc abc || tee tee apagar.txt apagar.txt abc tee : abc t rip $$ cat c s cat apagar.txt apagar.txt ell abc h S abc IPython __getat : tr__ def mastermind(guess, secret): def mastermind(guess, secret): """ """ Compara as strings de entrada e devolve um par de inteiros Compara as strings de entrada e devolve um par de inteiros (caracteres corretos na posição correta, (caracteres corretos na posição correta, caracteres corretos se desconsiderarmos a posição) caracteres corretos se desconsiderarmos a posição) Origem: https://gist.github.com/danilobellini/5311427 Origem: https://gist.github.com/danilobellini/5311427 """ """ return sum(1 for g, s in zip(guess, secret) if g == s), \ return sum(1 for g, s in zip(guess, secret) if g == s), \ sum(min(guess.count(x), secret.count(x)) for x in set(secret)) sum(min(guess.count(x), secret.count(x)) for x in set(secret)) class NameMastermind(object): class NameMastermind(object): def __init__(self): def __init__(self): size = randint(3, 8) size = randint(3, 8) name = "".join(choice(ascii_lowercase) for el in xrange(size)) name = "".join(choice(ascii_lowercase) for el in xrange(size)) self._name = name self._name = name setattr(self, name, lambda: "Yeah!") setattr(self, name, lambda: "Yeah!") def __getattr__(self, name): def __getattr__(self, name): return lambda: mastermind(name, self._name) return lambda: mastermind(name, self._name) ## -- End pasted text -## -- End pasted text -In [2]: game = NameMastermind() In [2]: game = NameMastermind() In [3]: game.abcd() In [3]: game.abcd() Out[3]: (0, 1) Out[3]: (0, 1) In [4]: game.a() In [4]: game.a() Out[4]: (0, 0) Out[4]: (0, 0) In [5]: game.b() In [5]: game.b() Out[5]: (0, 1) Out[5]: (0, 1) In [6]: game.ab() In [6]: game.ab() Out[6]: (0, 1) Out[6]: (0, 1) In [13]: game.urbrst() In [13]: game.urbrst() Out[13]: (5, 5) Out[13]: (5, 5) julia> f(x) = x * x julia> f(x) = x * x f (generic function with 1 method) f (generic function with 1 method) julia> code_llvm(f, (Float64,)) julia> code_llvm(f, (Float64,)) define double @julia_f_20166(double) { define double @julia_f_20166(double) { top: top: %1 = fmul double %0, %0, !dbg !857 %1 = fmul double %0, %0, !dbg !857 ret double %1, !dbg !857 ret double %1, !dbg !857 } } julia> code_native(f, (Float64,)) julia> code_native(f, (Float64,)) .text .text Filename: none Filename: none Source line: 1 Source line: 1 push RBP push RBP mov RBP, RSP mov RBP, RSP Source line: 1 Source line: 1 mulsd XMM0, XMM0 mulsd XMM0, XMM0 pop RBP pop RBP ret ret Julia: JIT julia> code_native(f, (Int,)) julia> code_native(f, (Int,)) .text .text Filename: none Filename: none Source line: 1 Source line: 1 push RBP push RBP mov RBP, RSP mov RBP, RSP Source line: 1 Source line: 1 imul RDI, RDI imul RDI, RDI mov RAX, RDI mov RAX, RDI pop RBP pop RBP ret ret In [8]: game.efgh In [8]: game.efgh Out[8]: <function __main__.<lambda>> In [14]: game.urbrrs() Out[8]: <function __main__.<lambda>> In [14]: game.urbrrs() Out[14]: (4, 6) Out[14]: (4, 6) In [9]: game.efgh() In [9]: game.efgh() Out[9]: (0, 0) In [15]: game.urbrsr() Out[9]: (0, 0) In [15]: game.urbrsr() Out[15]: 'Yeah!' brsr Out[15]: 'Yeah!' e ur d In [10]: game.efghA() e nt In [10]: game.efghA() ifere Out[10]: (0, 0) In [16]: game2 = NameMastermind() Out[10]: (0, 0) In [16]: game2 = NameMastermind() atório d ale alor In [11]: game.ijkl() In [17]: game2._name v m In [11]: game.ijkl() In [17]: game2._name Out[11]: (0, 0) Out[17]: 'vxbjqzy' Algu In [19]: from string import Template Out[11]: (0, 0) Out[17]: 'vxbjqzy' In [19]: from string import Template IPython: metaclass Danilo J. S. – @danilobellini – Tutorial: Adaptatividade em Python In Bellini [12]: game._name In [18]: game2.urbrsr() In [20]: Template.__metaclass__ In [12]: game._name In [18]: game2.urbrsr() In [20]: Template.__metaclass__ Out[12]: 'urbrsr' Out[18]: (1, 1) Out[20]: string._TemplateMetaclass Workshop de Tecnologia Adaptativa – São PauloOut[18]: – SP – (1, 2015-01-29 e 2015-01-30 Out[12]: 'urbrsr' 1) Out[20]: string._TemplateMetaclass In [7]: game.aab() In [7]: game.aab() Out[7]: (1, 1) Out[7]: (1, 1)