(2014-04-16) [Garoa HC] Strategy

download (2014-04-16) [Garoa HC] Strategy

If you can't read please download the document

description

Strategy (Design Pattern) e seu uso com dicionário e Multiton

Transcript of (2014-04-16) [Garoa HC] Strategy

  • 1. 2014-04-16 Strategy Design patterns em linguagens dinmicas Danilo J. S. Bellini Strategy (Design Pattern)Strategy (Design Pattern) e seu uso com dicionrio e Multitone seu uso com dicionrio e Multiton Segunda reunio em 2014 do grupo de estudos deSegunda reunio em 2014 do grupo de estudos de Design patterns em linguagens dinmicasDesign patterns em linguagens dinmicas nono Garoa Hacker ClubeGaroa Hacker Clube Slides com a preparao deSlides com a preparao de Danilo J. S. BelliniDanilo J. S. Bellini para discusso durante reuniopara discusso durante reunio 2014-04-162014-04-16 Cdigo dos slides disponvel em: https://github.com/danilobellini/design_patterns

2. 2014-04-16 Strategy Design patterns em linguagens dinmicas Danilo J. S. Bellini ProblemaProblema Muitas solues para um mesmo problemaMuitas solues para um mesmo problema Ordenao (sort), programao no-linear,Ordenao (sort), programao no-linear, reconhecimento de padres, otimizao,reconhecimento de padres, otimizao, processamento de sinais, processamento de sinais, Famlia de algoritmosFamlia de algoritmos Deciso em tempo de execuo do algoritmo aDeciso em tempo de execuo do algoritmo a ser utilizadoser utilizado 3. 2014-04-16 Strategy Design patterns em linguagens dinmicas Danilo J. S. Bellini StrategyStrategy 3 conceitos:3 conceitos: InterfaceInterface EstratgiasEstratgias Contexto (de uso)Contexto (de uso) 4. 2014-04-16 Strategy Design patterns em linguagens dinmicas Danilo J. S. Bellini UML?! Muito chato! Cad o cdigo?UML?! Muito chato! Cad o cdigo? No nested o suficiente? #!/usr/bin/env python3 from abc import ABCMeta, abstractmethod class Estratgia(metaclass=ABCMeta): @abstractmethod def executar(self, a, b): # Dois inteiros pass class Soma(Estratgia): def executar(self, a, b): return a + b class Subtrao(Estratgia): def executar(self, a, b): return a - b class Multiplicao(Estratgia): def executar(self, a, b): return a * b class Contexto: def __init__(self, estratgia, smbolo): self.estratgia = estratgia self.smbolo = smbolo def tarefa(self, a, b): resultado = self.estratgia.executar(a, b) args = (a, self.smbolo, b, resultado) print("{} {} {} = {}".format(*args)) #!/usr/bin/env python3 from abc import ABCMeta, abstractmethod class Estratgia(metaclass=ABCMeta): @abstractmethod def executar(self, a, b): # Dois inteiros pass class Soma(Estratgia): def executar(self, a, b): return a + b class Subtrao(Estratgia): def executar(self, a, b): return a - b class Multiplicao(Estratgia): def executar(self, a, b): return a * b class Contexto: def __init__(self, estratgia, smbolo): self.estratgia = estratgia self.smbolo = smbolo def tarefa(self, a, b): resultado = self.estratgia.executar(a, b) args = (a, self.smbolo, b, resultado) print("{} {} {} = {}".format(*args)) if __name__ == "__main__": Contexto(Soma(), "+").tarefa(22, 3) ctx = Contexto(Subtrao(), "-") ctx.tarefa(22, 3) ctx.estratgia = Multiplicao() ctx.smbolo = "*" ctx.tarefa(22, 3) if __name__ == "__main__": Contexto(Soma(), "+").tarefa(22, 3) ctx = Contexto(Subtrao(), "-") ctx.tarefa(22, 3) ctx.estratgia = Multiplicao() ctx.smbolo = "*" ctx.tarefa(22, 3) strategy_0.py 5. 2014-04-16 Strategy Design patterns em linguagens dinmicas Danilo J. S. Bellini Burocracia... necessria?Burocracia... necessria? Definio de classes para cada algoritmoDefinio de classes para cada algoritmo Mais de uma instncia de um nico algoritmo?Mais de uma instncia de um nico algoritmo? Memria (efeito colaterais)?Memria (efeito colaterais)? Instante da instanciao?Instante da instanciao? Definio explcita da interfaceDefinio explcita da interface Tipos estticos e explcitos?Tipos estticos e explcitos? Quantidades/nomes de argumentos sempreQuantidades/nomes de argumentos sempre idnticos?idnticos? Orientao a objetos?Orientao a objetos? No!No! 6. 2014-04-16 Strategy Design patterns em linguagens dinmicas Danilo J. S. Bellini InterfaceInterface em Pythonem Python Duck typingDuck typing When I see a bird thatWhen I see a bird that walks like a duck andwalks like a duck and swims like a duck andswims like a duck and quacks like a duck,quacks like a duck, I call that bird a duck.I call that bird a duck. ABC (Abstract Base Classes)ABC (Abstract Base Classes) Dunder __subclasshook__Dunder __subclasshook__ 7. 2014-04-16 Strategy Design patterns em linguagens dinmicas Danilo J. S. Bellini #!/usr/bin/env python3 class Soma: def executar(self, a, b): return a + b class Subtrao: def executar(self, a, b): return a - b class Multiplicao: def executar(self, a, b): return a * b class Contexto: def __init__(self, estratgia, smbolo): self.estratgia = estratgia self.smbolo = smbolo def tarefa(self, a, b): resultado = self.estratgia.executar(a, b) args = (a, self.smbolo, b, resultado) print("{} {} {} = {}".format(*args)) #!/usr/bin/env python3 class Soma: def executar(self, a, b): return a + b class Subtrao: def executar(self, a, b): return a - b class Multiplicao: def executar(self, a, b): return a * b class Contexto: def __init__(self, estratgia, smbolo): self.estratgia = estratgia self.smbolo = smbolo def tarefa(self, a, b): resultado = self.estratgia.executar(a, b) args = (a, self.smbolo, b, resultado) print("{} {} {} = {}".format(*args)) if __name__ == "__main__": Contexto(Soma(), "+").tarefa(22, 3) ctx = Contexto(Subtrao(), "-") ctx.tarefa(22, 3) ctx.estratgia = Multiplicao() ctx.smbolo = "*" ctx.tarefa(22, 3) if __name__ == "__main__": Contexto(Soma(), "+").tarefa(22, 3) ctx = Contexto(Subtrao(), "-") ctx.tarefa(22, 3) ctx.estratgia = Multiplicao() ctx.smbolo = "*" ctx.tarefa(22, 3) Sem classe abstrataSem classe abstrata (interface implcita)(interface implcita) strategy_1.py 8. 2014-04-16 Strategy Design patterns em linguagens dinmicas Danilo J. S. Bellini #!/usr/bin/env python3 from abc import ABCMeta, abstractmethod class Estratgia(metaclass=ABCMeta): @abstractmethod def executar(self, a, b): # Dois inteiros pass @classmethod def __subclasshook__(cls, C): return any("executar" in vars(B) for B in C.mro()) or NotImplemented class Soma: def executar(self, a, b): return a + b print(isinstance(Soma(), Estratgia)) # True print(issubclass(Soma, Estratgia)) # True #!/usr/bin/env python3 from abc import ABCMeta, abstractmethod class Estratgia(metaclass=ABCMeta): @abstractmethod def executar(self, a, b): # Dois inteiros pass @classmethod def __subclasshook__(cls, C): return any("executar" in vars(B) for B in C.mro()) or NotImplemented class Soma: def executar(self, a, b): return a + b print(isinstance(Soma(), Estratgia)) # True print(issubclass(Soma, Estratgia)) # True Subclass HookSubclass Hook subclasshook.py In [5]: B.mro() Out[5]: [__main__.B, __main__.A, builtins.object] In [6]: D.mro() # MRO = Method Resolution Order Out[6]: [__main__.D, __main__.B, __main__.C, __main__.A, builtins.object] In [7]: vars(B) # Atributos (mtodos, propriedades, etc.) Out[7]: dict_proxy({'__module__': '__main__', '__doc__': None}) In [5]: B.mro() Out[5]: [__main__.B, __main__.A, builtins.object] In [6]: D.mro() # MRO = Method Resolution Order Out[6]: [__main__.D, __main__.B, __main__.C, __main__.A, builtins.object] In [7]: vars(B) # Atributos (mtodos, propriedades, etc.) Out[7]: dict_proxy({'__module__': '__main__', '__doc__': None}) In [1]: class A: pass In [2]: class B(A): pass In [3]: class C(A): pass In [4]: class D(B, C): pass In [1]: class A: pass In [2]: class B(A): pass In [3]: class C(A): pass In [4]: class D(B, C): pass O que MRO? 9. 2014-04-16 Strategy Design patterns em linguagens dinmicas Danilo J. S. Bellini EstratgiaEstratgia Qualquer callableQualquer callable Mtodo, funo, __call__, operadores, etc.Mtodo, funo, __call__, operadores, etc. Funes como objetos de segunda classeFunes como objetos de segunda classe Utilizao do mtodo de um objetosUtilizao do mtodo de um objetos Classes para possibilitar objetosClasses para possibilitar objetos InstanciaoInstanciao Python, Ruby, JavaScript, Python, Ruby, JavaScript, Funes de primeira classeFunes de primeira classe Funes de ordem superiorFunes de ordem superior Fechamentos (closures)Fechamentos (closures) 10. 2014-04-16 Strategy Design patterns em linguagens dinmicas Danilo J. S. Bellini #!/usr/bin/env python3 def soma(a, b): return a + b def subtrao(a, b): return a - b def multiplicao(a, b): return a * b class Contexto: def __init__(self, estratgia, smbolo): self.estratgia = estratgia self.smbolo = smbolo def tarefa(self, a, b): resultado = self.estratgia(a, b) args = (a, self.smbolo, b, resultado) print("{} {} {} = {}".format(*args)) #!/usr/bin/env python3 def soma(a, b): return a + b def subtrao(a, b): return a - b def multiplicao(a, b): return a * b class Contexto: def __init__(self, estratgia, smbolo): self.estratgia = estratgia self.smbolo = smbolo def tarefa(self, a, b): resultado = self.estratgia(a, b) args = (a, self.smbolo, b, resultado) print("{} {} {} = {}".format(*args)) if __name__ == "__main__": Contexto(soma, "+").tarefa(22, 3) ctx = Contexto(subtrao, "-") ctx.tarefa(22, 3) ctx.estratgia = multiplicao ctx.smbolo = "*" ctx.tarefa(22, 3) if __name__ == "__main__": Contexto(soma, "+").tarefa(22, 3) ctx = Contexto(subtrao, "-") ctx.tarefa(22, 3) ctx.estratgia = multiplicao ctx.smbolo = "*" ctx.tarefa(22, 3) Funes de primeira classe!Funes de primeira classe! strategy_2.py Funes comoFunes como valores (ou objetos)valores (ou objetos) CallbackCallback HandlerHandler 11. 2014-04-16 Strategy Design patterns em linguagens dinmicas Danilo J. S. Bellini PythonPython lambda e namedtuplelambda e namedtuple #!/usr/bin/env python3 from collections import namedtuple Strategy = namedtuple("Strategy", ["func", "symbol"]) sum = lambda a, b: a + b sub = lambda a, b: a - b mul = lambda a, b: a * b apply = lambda st, a, b: st.func(a, b) strategies = [Strategy(sum, "+"), Strategy(sub, "-"), Strategy(mul, "*")] for st in strategies: print("2 %c 3 = %d" % (st.symbol, apply(st, 2, 3))) print("7 %c 5 = %d" % (st.symbol, apply(st, 7, 5))) #!/usr/bin/env python3 from collections import namedtuple Strategy = namedtuple("Strategy", ["func", "symbol"]) sum = lambda a, b: a + b sub = lambda a, b: a - b mul = lambda a, b: a * b apply = lambda st, a, b: st.func(a, b) strategies = [Strategy(sum, "+"), Strategy(sub, "-"), Strategy(mul, "*")] for st in strategies: print("2 %c 3 = %d" % (st.symbol, apply(st, 2, 3))) print("7 %c 5 = %d" % (st.symbol, apply(st, 7, 5))) strategy_3.py 12. 2014-04-16 Strategy Design patterns em linguagens dinmicas Danilo J. S. Bellini CC #include typedef int (*BinaryOpPtr)(int, int); typedef struct{ BinaryOpPtr func; char symbol; } Strategy; int sum(int a, int b){ return a + b; } int sub(int a, int b){ return a - b; } int mul(int a, int b){ return a * b; } int apply(Strategy st, int a, int b){ return st.func(a, b); } Strategy strategies[] = {{sum, '+'}, {sub, '-'}, {mul, '*'}}; int main(){ Strategy st; int idx; for(idx = 0; idx < 3; idx++){ st = strategies[idx]; printf("2 %c 3 = %dn", st.symbol, apply(st, 2, 3)); printf("7 %c 5 = %dn", st.symbol, apply(st, 7, 5)); } return 0; } #include typedef int (*BinaryOpPtr)(int, int); typedef struct{ BinaryOpPtr func; char symbol; } Strategy; int sum(int a, int b){ return a + b; } int sub(int a, int b){ return a - b; } int mul(int a, int b){ return a * b; } int apply(Strategy st, int a, int b){ return st.func(a, b); } Strategy strategies[] = {{sum, '+'}, {sub, '-'}, {mul, '*'}}; int main(){ Strategy st; int idx; for(idx = 0; idx < 3; idx++){ st = strategies[idx]; printf("2 %c 3 = %dn", st.symbol, apply(st, 2, 3)); printf("7 %c 5 = %dn", st.symbol, apply(st, 7, 5)); } return 0; } O mesmo exemplo do slide anterior strategy.c 13. 2014-04-16 Strategy Design patterns em linguagens dinmicas Danilo J. S. Bellini ScipyScipy (e muitos outros)(e muitos outros) Duas maneiras diferentes de resolver o problema:Duas maneiras diferentes de resolver o problema: Diversas funes dispersas com assinatura similar, usar noDiversas funes dispersas com assinatura similar, usar no contextocontexto e.g. funes do scipy.optimizee.g. funes do scipy.optimize String com o nome da estratgia como parmetroString com o nome da estratgia como parmetro e.g. projeto de filtros IIR com o scipy.signal.iirdesigne.g. projeto de filtros IIR com o scipy.signal.iirdesign ftype = ellipftype = ellip Elptico Elptico ftype = butterftype = butter Butterworth Butterworth ftype = cheby1ftype = cheby1 Chebyshev I Chebyshev I ftype = cheby2ftype = cheby2 Chebyshev II Chebyshev II ftype = besselftype = bessel Bessel Bessel Sada chaveada pela stringSada chaveada pela string baba ouou zpkzpk no parmetro de entradano parmetro de entrada outputoutput Em alguns casos (e.g. fmin), possvel usar os nomes com o built-in dir para possveis consultas Estratgias existentes apresentadas em docstring 14. 2014-04-16 Strategy Design patterns em linguagens dinmicas Danilo J. S. Bellini ContextoContexto Completamente aberto, basta que utilize ouCompletamente aberto, basta que utilize ou possa utilizar a estratgiapossa utilizar a estratgia Executa o algoritmoExecuta o algoritmo Outros possveis requisitos, reflection CRUD:Outros possveis requisitos, reflection CRUD: Conhecer/consultar todas as estratgiasConhecer/consultar todas as estratgias Varrer por todas as estratgiasVarrer por todas as estratgias Modificar, remover ou criar novas estratgiasModificar, remover ou criar novas estratgias 15. 2014-04-16 Strategy Design patterns em linguagens dinmicas Danilo J. S. Bellini DicionrioDicionrio (Mapping / Hash / Vetor associativo)(Mapping / Hash / Vetor associativo) Pares chave-valorPares chave-valor Sem ordenaoSem ordenao VariantesVariantes OrderedDictOrderedDict defaultdictdefaultdict 3 mtodos para iterar:3 mtodos para iterar: Dict.keys()Dict.keys() Dict.values()Dict.values() Dict.items()Dict.items() Pares chave, valor Pares chave, valor #!/usr/bin/env python3 strategies = { "+": lambda a, b: a + b, "-": lambda a, b: a - b, "*": lambda a, b: a * b, } for key, value in strategies.items(): print("2 %c 3 = %d" % (key, value(2, 3))) print("7 %c 5 = %d" % (key, value(7, 5))) #!/usr/bin/env python3 strategies = { "+": lambda a, b: a + b, "-": lambda a, b: a - b, "*": lambda a, b: a * b, } for key, value in strategies.items(): print("2 %c 3 = %d" % (key, value(2, 3))) print("7 %c 5 = %d" % (key, value(7, 5))) strategy_4.py No Python 3, devolvem iterveis. No Python 2, devolvem listas, mas os mtodos com prefixo iter (e.g. dict.iteritems()) devolvem iteradores. Funo (annima) como valor 16. 2014-04-16 Strategy Design patterns em linguagens dinmicas Danilo J. S. Bellini E quando o elemento com aE quando o elemento com a chave no est no dicionrio?chave no est no dicionrio? __missing____missing__ Criar elemento?Criar elemento? Responder?Responder? H anlogos:H anlogos: __getitem____getitem__ __getattr____getattr__ #!/usr/bin/env python3 class MeuDicionrio(dict): def __missing__(self, chave): if chave.strip() != chave: valor = self[chave.strip()] self[chave] = valor return valor raise KeyError("Not Found") estratgias = MeuDicionrio([ ("+", lambda a, b: a + b), ("-", lambda a, b: a - b), ("*", lambda a, b: a * b), ]) for smbolo in ["+", " +", " - ", "n* n"]: funo = estratgias[smbolo] print("2 %s 3 = %d" % (smbolo, funo(2, 3))) print("7 %s 5 = %d" % (smbolo, funo(7, 5))) print(estratgias["/"]) #!/usr/bin/env python3 class MeuDicionrio(dict): def __missing__(self, chave): if chave.strip() != chave: valor = self[chave.strip()] self[chave] = valor return valor raise KeyError("Not Found") estratgias = MeuDicionrio([ ("+", lambda a, b: a + b), ("-", lambda a, b: a - b), ("*", lambda a, b: a * b), ]) for smbolo in ["+", " +", " - ", "n* n"]: funo = estratgias[smbolo] print("2 %s 3 = %d" % (smbolo, funo(2, 3))) print("7 %s 5 = %d" % (smbolo, funo(7, 5))) print(estratgias["/"]) missing.py 17. 2014-04-16 Strategy Design patterns em linguagens dinmicas Danilo J. S. Bellini MultitonMultiton Multi + singletonMulti + singleton Registro deRegistro de singletonssingletons LazyLazy Similar aoSimilar ao dicionriodicionrio Valores definidosValores definidos (ou instanciados)(ou instanciados) no primeiro usono primeiro uso CacheCache #!/usr/bin/env python3 op = { "+": lambda a, b: a + b, "-": lambda a, b: a - b, "*": lambda a, b: a * b, } class MeuDicionrio(dict): def __missing__(self, chave): if chave[0] in op: b = int(chave[1:]) func = op[chave[0]] resultado = lambda a: func(a, b) else: a = int(chave[:-1]) func = op[chave[-1]] resultado = lambda b: func(a, b) self[chave] = resultado return resultado estratgias = MeuDicionrio() for smbolo in ["+2", "5-", "-3", "4*", "+2"]: funo = estratgias[smbolo] print("2, %s -> %d" % (smbolo, funo(2))) print("-1, %s -> %d" % (smbolo, funo(-1))) print("7, %s -> %d" % (smbolo, funo(7))) #!/usr/bin/env python3 op = { "+": lambda a, b: a + b, "-": lambda a, b: a - b, "*": lambda a, b: a * b, } class MeuDicionrio(dict): def __missing__(self, chave): if chave[0] in op: b = int(chave[1:]) func = op[chave[0]] resultado = lambda a: func(a, b) else: a = int(chave[:-1]) func = op[chave[-1]] resultado = lambda b: func(a, b) self[chave] = resultado return resultado estratgias = MeuDicionrio() for smbolo in ["+2", "5-", "-3", "4*", "+2"]: funo = estratgias[smbolo] print("2, %s -> %d" % (smbolo, funo(2))) print("-1, %s -> %d" % (smbolo, funo(-1))) print("7, %s -> %d" % (smbolo, funo(7))) multiton.py Closure 18. 2014-04-16 Strategy Design patterns em linguagens dinmicas Danilo J. S. Bellini AudioLazyAudioLazy StrategyDict (dicionrio de estratgias)StrategyDict (dicionrio de estratgias) ItervelItervel Padro por estratgias (valores)Padro por estratgias (valores) Mltiplas chaves (nomes)Mltiplas chaves (nomes) e.g.e.g. window[rectangular] is window[rect]window[rectangular] is window[rect] True True Acesso como atributosAcesso como atributos e.g.e.g. lowpass.pole_exp(pi/7)lowpass.pole_exp(pi/7) Estratgia padro do dicionrioEstratgia padro do dicionrio Consultvel no atributo defaultConsultvel no atributo default e.g.e.g. lowpass(pi/7)lowpass(pi/7) equivale aequivale a lowpass.pole(pi/7)lowpass.pole(pi/7) Docstrings com resumos automticos das docstrings das estratgias 19. 2014-04-16 Strategy Design patterns em linguagens dinmicas Danilo J. S. Bellini Uso da StrategyDict na AudioLazyUso da StrategyDict na AudioLazy In [1]: import audiolazy In [2]: from audiolazy import StrategyDict In [3]: [name for name in dir(audiolazy) ...: if isinstance(getattr(audiolazy, name), StrategyDict)] Out[3]: ['accumulate', 'almost_eq', 'chain', 'chunks', 'comb', 'envelope', 'erb', 'float_str', 'gammatone', 'highpass', 'izip', 'lagrange', 'lowpass', 'lpc', 'maverage', 'resonator', 'window'] In [4]: len(_) # Total de nomes de instncias de StrategyDict na AudioLazy Out[4]: 17 In [5]: len(audiolazy.window) # Todos atualmente com pelo menos 2 estratgias Out[5]: 6 In [1]: import audiolazy In [2]: from audiolazy import StrategyDict In [3]: [name for name in dir(audiolazy) ...: if isinstance(getattr(audiolazy, name), StrategyDict)] Out[3]: ['accumulate', 'almost_eq', 'chain', 'chunks', 'comb', 'envelope', 'erb', 'float_str', 'gammatone', 'highpass', 'izip', 'lagrange', 'lowpass', 'lpc', 'maverage', 'resonator', 'window'] In [4]: len(_) # Total de nomes de instncias de StrategyDict na AudioLazy Out[4]: 17 In [5]: len(audiolazy.window) # Todos atualmente com pelo menos 2 estratgias Out[5]: 6 20. 2014-04-16 Strategy Design patterns em linguagens dinmicas Danilo J. S. Bellini strategy_sort.py #!/usr/bin/env python3 from operator import itemgetter from audiolazy import StrategyDict sort = StrategyDict("sort") @sort.strategy("slow", "bad") def sort(data): for idx, el in enumerate(data): idx_min, el_min = min(enumerate(data[idx:], idx), key=itemgetter(1)) data[idx], data[idx_min] = el_min, el @sort.strategy("bubble") def sort(data): . . . @sort.strategy("merge") def sort(data): . . . #!/usr/bin/env python3 from operator import itemgetter from audiolazy import StrategyDict sort = StrategyDict("sort") @sort.strategy("slow", "bad") def sort(data): for idx, el in enumerate(data): idx_min, el_min = min(enumerate(data[idx:], idx), key=itemgetter(1)) data[idx], data[idx_min] = el_min, el @sort.strategy("bubble") def sort(data): . . . @sort.strategy("merge") def sort(data): . . . if __name__ == "__main__": from random import shuffle data = list(range(30)) print(sort.default.__name__) # slow (primeira) print() print(sort.bubble is sort["bubble"]) # True print() print(sort) # As 3 estratgias for st in sort: print() shuffle(data) print(data) # Scrambled st(data) print(data) # [0, 1, 2, ...] if __name__ == "__main__": from random import shuffle data = list(range(30)) print(sort.default.__name__) # slow (primeira) print() print(sort.bubble is sort["bubble"]) # True print() print(sort) # As 3 estratgias for st in sort: print() shuffle(data) print(data) # Scrambled st(data) print(data) # [0, 1, 2, ...] Contexto Exemplo: Ordenao com um StrategyDict slow: Ordenar coletando valores mnimos bubble: Alone bubble sort merge: Merge sort 21. 2014-04-16 Strategy Design patterns em linguagens dinmicas Danilo J. S. Bellini Fim!Fim! Obrigado =) Repositrio com o cdigo dos slides: https://github.com/danilobellini/design_patterns