Un mémo qui interprète l'article enseigné par la personne à côté de moi.
Une usine est définie pour une cible. Le code ci-dessous est à peu près le même que celui de l'article d'origine, avec des modifications pour donner des arguments nommés.
python
class Hoge(object):
def __init__(self, args1, kwargs1='test1'):
self.args1 = args1
self.kwargs1 = kwargs1
class HogeFactory(object):
def __init__(self):
self._instances = {}
def get_instance(self, args1, kwargs1):
if ((args1), (kwargs1)) not in self._instances:
self._instances[((args1), (kwargs1))] = Hoge(args1, kwargs1=kwargs1)
return self._instances[((args1), (kwargs1))]
class Piyo(object):
def __init__(self, args1, kwargs1, kwargs2):
self.args1 = args1
self.kwargs1 = kwargs1
self.kwargs2 = kwargs2
class PiyoFactory(object):
def __init__(self):
self._instances = {}
def get_instance(self, args1, kwargs1='test1', kwargs2='test2'):
if ((args1), (kwargs1, kwargs2)) not in self._instances:
self._instances[((args1), (kwargs1, kwargs2))] = Piyo(args1, kwargs1=kwargs1, kwargs2=kwargs2)
return self._instances[((args1), (kwargs1, kwargs2))]
hogeFactory = HogeFactory()
piyoFactory = PiyoFactory()
assert hogeFactory.get_instance(1, kwargs1=2) is hogeFactory.get_instance(1, kwargs1=2)
assert hogeFactory.get_instance(1, kwargs1=2) is not hogeFactory.get_instance(1, kwargs1=3)
assert piyoFactory.get_instance('a', kwargs1='b', kwargs2='c') is piyoFactory.get_instance('a', kwargs1='b', kwargs2='c')
assert piyoFactory.get_instance('a', kwargs1='b', kwargs2='c') is not piyoFactory.get_instance('a', kwargs1='b', kwargs2='d')
La raison pour laquelle une fabrique était nécessaire pour une cible était que le constructeur de chaque cible avait une signature différente = la méthode get_instance
avait une signature différente.
Vous pouvez utiliser les arguments de longueur variable de Python pour absorber les différences de signatures.
python
class FlyweightFactory(object):
def __init__(self, cls):
self._instances = {}
self._cls = cls
def get_instance(self, *args, **kwargs):
return self._instances.setdefault((args, tuple(kwargs.items())), self._cls(*args, **kwargs))
class Hoge(object):
def __init__(self, args1, kwargs1='test1'):
self.args1 = args1
self.kwargs1 = kwargs1
class Piyo(object):
def __init__(self, args1, kwargs1, kwargs2):
self.args1 = args1
self.kwargs1 = kwargs1
self.kwargs2 = kwargs2
hogeFactory = FlyweightFactory(Hoge)
piyoFactory = FlyweightFactory(Piyo)
assert hogeFactory.get_instance(1, kwargs1=2) is hogeFactory.get_instance(1, kwargs1=2)
assert hogeFactory.get_instance(1, kwargs1=2) is not hogeFactory.get_instance(1, kwargs1=3)
assert piyoFactory.get_instance('a', kwargs1='b', kwargs2='c') is piyoFactory.get_instance('a', kwargs1='b', kwargs2='c')
assert piyoFactory.get_instance('a', kwargs1='b', kwargs2='c') is not piyoFactory.get_instance('a', kwargs1='b', kwargs2='d')
Si vous décorez la Flyweight Factory
ci-dessus, vous pouvez l'écrire plus simplement.
python
class Flyweight(object):
def __init__(self, cls):
self._instances = {}
self._cls = cls
def __call__(self, *args, **kwargs):
return self._instances.setdefault((args, tuple(kwargs.items())), self._cls(*args, **kwargs))
@Flyweight
class Hoge(object):
def __init__(self, args1, kwargs1='test1'):
self.args1 = args1
self.kwargs1 = kwargs1
@Flyweight
class Piyo(object):
def __init__(self, args1, kwargs1, kwargs2):
self.args1 = args1
self.kwargs1 = kwargs1
self.kwargs2 = kwargs2
assert Hoge(1, kwargs1=2) is Hoge(1, kwargs1=2)
assert Hoge(1, kwargs1=2) is not Hoge(1, kwargs1=3)
assert Piyo('a', kwargs1='b', kwargs2='c') is Piyo('a', kwargs1='b', kwargs2='c')
assert Piyo('a', kwargs1='b', kwargs2='c') is not Piyo('a', kwargs1='b', kwargs2='d')
Le décorateur est appelé en prenant la cible attachée comme argument. Le code équivalent à ce processus peut être écrit comme suit sans utiliser de décorateur.
Au lieu d'appeler get_instance
, il y a une différence dans la lecture de l'instance Flyweight en tant que fonction, mais vous pouvez voir qu'elle n'est pas si différente de l'extrait de code précédent.
(ref. http://docs.python.jp/3.4/reference/datamodel.html#object.call)
python
class Flyweight(object):
def __init__(self, cls):
self._instances = {}
self._cls = cls
def __call__(self, *args, **kwargs):
return self._instances.setdefault((args, tuple(kwargs.items())), self._cls(*args, **kwargs))
class Hoge(object):
def __init__(self, args1, kwargs1='test1'):
self.args1 = args1
self.kwargs1 = kwargs1
class Piyo(object):
def __init__(self, args1, kwargs1, kwargs2):
self.args1 = args1
self.kwargs1 = kwargs1
self.kwargs2 = kwargs2
# Hoge,Prenez Piyo comme argument,Créez une instance Flyweight.
Hoge = Flyweight(Hoge)
Piyo = Flyweight(Piyo)
assert Hoge(1, kwargs1=2) is Hoge(1, kwargs1=2)
assert Hoge(1, kwargs1=2) is not Hoge(1, kwargs1=3)
assert Piyo('a', kwargs1='b', kwargs2='c') is Piyo('a', kwargs1='b', kwargs2='c')
assert Piyo('a', kwargs1='b', kwargs2='c') is not Piyo('a', kwargs1='b', kwargs2='d')
De plus, l'implémentation utilisant la fermeture suivante est qu'elle n'a pas besoin d'être une classe tant qu'elle est appelable.
python
def flyweight(cls):
instances = {}
return lambda *args, **kwargs: instances.setdefault((args, tuple(kwargs.items())), cls(*args, **kwargs))
@flyweight
class Hoge(object):
def __init__(self, args1, kwargs1='test1'):
self.args1 = args1
self.kwargs1 = kwargs1
@flyweight
class Piyo(object):
def __init__(self, args1, kwargs1, kwargs2):
self.args1 = args1
self.kwargs1 = kwargs1
self.kwargs2 = kwargs2
assert Hoge(1, kwargs1=2) is Hoge(1, kwargs1=2)
assert Hoge(1, kwargs1=2) is not Hoge(1, kwargs1=3)
assert Piyo('a', kwargs1='b', kwargs2='c') is Piyo('a', kwargs1='b', kwargs2='c')
assert Piyo('a', kwargs1='b', kwargs2='c') is not Piyo('a', kwargs1='b', kwargs2='d')
Le code équivalent sans décorateurs ressemble à ceci:
python
def flyweight(cls):
instances = {}
return lambda *args, **kwargs: instances.setdefault((args, tuple(kwargs.items())), cls(*args, **kwargs))
class Hoge(object):
def __init__(self, args1, kwargs1='test1'):
self.args1 = args1
self.kwargs1 = kwargs1
class Piyo(object):
def __init__(self, args1, kwargs1, kwargs2):
self.args1 = args1
self.kwargs1 = kwargs1
self.kwargs2 = kwargs2
Hoge = flyweight(Hoge)
Piyo = flyweight(Piyo)
assert Hoge(1, kwargs1=2) is Hoge(1, kwargs1=2)
assert Hoge(1, kwargs1=2) is not Hoge(1, kwargs1=3)
assert Piyo('a', kwargs1='b', kwargs2='c') is Piyo('a', kwargs1='b', kwargs2='c')
assert Piyo('a', kwargs1='b', kwargs2='c') is not Piyo('a', kwargs1='b', kwargs2='d')
Cette méthode est très intelligente, mais elle a l'inconvénient de ne pas pouvoir créer de sous-classes de la classe Hoge
/ Piyo
.
C'est parce que le Hoge
/ Piyo
n'est plus une classe, mais un objet fonction, comme vous pouvez le voir dans le code qui n'utilise pas de décorateurs. Par conséquent, le code suivant entraînera une erreur.
python
def flyweight(cls):
instances = {}
return lambda *args, **kwargs: instances.setdefault((args, tuple(kwargs.items())), cls(*args, **kwargs))
@flyweight
class Hoge(object):
def __init__(self, args1, kwargs1='test1'):
self.args1 = args1
self.kwargs1 = kwargs1
@flyweight
class Piyo(object):
def __init__(self, args1, kwargs1, kwargs2):
self.args1 = args1
self.kwargs1 = kwargs1
self.kwargs2 = kwargs2
#Hoge n'est plus une classe et ne peut pas être hérité
class Hoge2(Hoge):
def __init__(self, args1, kwargs1='test1'):
super().__init__(args1, kwargs1=kwargs1)
Dans le sens où Hoge
/ Piyo
n'est plus une classe à cause du décorateur, il n'y a aucune différence lors de l'expression du décorateur en tant que classe, mais il existe une instance de la classe Flyweight
qui remplace la classe Hoge
/ Piyo
. C'est le miso qui détient la classe d'origine.
Par conséquent, il peut être sous-classé avec le code suivant.
python
class Flyweight(object):
def __init__(self, cls):
self._instances = {}
self._cls = cls
def __call__(self, *args, **kwargs):
return self._instances.setdefault((args, tuple(kwargs.items())), self._cls(*args, **kwargs))
@Flyweight
class Hoge(object):
def __init__(self, args1, kwargs1='test1'):
self.args1 = args1
self.kwargs1 = kwargs1
@Flyweight
class Piyo(object):
def __init__(self, args1, kwargs1, kwargs2):
self.args1 = args1
self.kwargs1 = kwargs1
self.kwargs2 = kwargs2
# Hoge(L'entité est une instance Flyweight)Est une variable d'instance qui contient la classe d'origine_Avoir des cls
class Hoge2(Hoge._cls):
def __init__(self, args1, kwargs1='test1'):
super().__init__(args1, kwargs1=kwargs1)
Recommended Posts