Nella mia esperienza, alcuni Pattern sono ancora utili in Python e sono ancora più facili da configurare rispetto a linguaggi più statici. Alcuni Pattern OTOH non sono solo necessari, o persino disapprovati, come il Pattern Singleton. Utilizzare invece una variabile di livello modulo o una funzione. Oppure usa il modello Borg.
Invece di creare un Pattern Creazionale è spesso sufficiente passare un callable attorno a ciò che crea oggetti. Potrebbe trattarsi di una funzione, un oggetto con un metodo __call__
o anche una classe, poiché non esiste new()
in Python, solo un'invocazione della classe stessa:
def make_da_thing(maker, other, stuff):
da_thing = maker(other + 1, stuff + 2)
# ... do sth
return da_thing
def maker_func(x, y):
return x * y
class MakerClass(object):
def __init__(self, x, y):
self.x = x
self.y = y
...
a = make_da_thing(maker_func, 5, 8)
b = make_da_thing(MakerClass, 6, 7)
Lo stato e il modello di strategia condividono una struttura molto simile in linguaggi come C ++ e Java. Meno in Python. Il Pattern di strategia rimane più o meno lo stesso, ma State Pattern diventa per lo più inutile. State Pattern nei linguaggi statici simula il cambio di classe in fase di esecuzione. In Python, puoi fare proprio questo: cambiare la classe di un oggetto in fase di runtime. Finché lo fai in modo controllato e incapsulato, dovresti stare bene:
class On(object):
is_on = True
def switch(self):
self.__class__ = Off
class Off(object):
is_on = False
def switch(self):
self.__class__ = On
...
my_switch = On()
assert my_switch.is_on
my_switch.switch()
assert not my_switch.is_on
I pattern che fanno affidamento su Static Type Dispatch non funzioneranno, o funzionano in modo abbastanza diverso. Non è necessario scrivere tanto codice della piastra della caldaia, ad es. Visitor Pattern: in Java e C ++ devi scrivere un metodo accept in ogni classe visitabile, mentre in Python puoi ereditare quella funzionalità attraverso una classe di mixin, come Visitable:
class Visitable(object):
def accept(self, visitor):
visit = getattr(visitor, 'visit' + self.__class__.__name__)
return visit(self)
...
class On(Visitable):
''' exactly like above '''
class Off(Visitable):
''' exactly like above '''
class SwitchStatePrinter(object): # Visitor
def visitOn(self, switch):
print 'the switch is on'
def visitOff(self, switch):
print 'the switch is off'
class SwitchAllOff(object): # Visitor
def visitOn(self, switch):
switch.switch()
def visitOff(self, switch):
pass
...
print_state = SwitchStatePrinter()
turn_em_off = SwitchAllOff()
for each in my_switches:
each.accept(print_state)
each.accept(turn_em_off)
Molte situazioni che richiedono l'applicazione di un Pattern in un linguaggio statico non lo fanno tanto in Python. Molte cose possono essere risolte con altre tecniche, come le funzioni di ordine superiore (decoratori, fabbriche di funzioni) o le meta-classi.