Changez le modèle Flyweight en Pythonic (?) (1)

Un mémo qui interprète l'article enseigné par la personne à côté de moi.

Modèle Flyweight fidèle au GoF

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')

Généralisation d'usine avec des arguments de longueur variable

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')

Décorateur d'usine (classe)

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')

Décorateur d'usine (plus près)

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

Changez le modèle Flyweight en Pythonic (?) (3)
Changez le modèle Flyweight en Pythonic (?) (2)
Changez le modèle Flyweight en Pythonic (?) (1)
[Python] Changer l'alphabet en nombre
Apprenez le modèle de conception "Flyweight" en Python
Script pour changer la description de fasta
[Python] Comment changer le format de la date (format d'affichage)
Modifiez le point décimal de la journalisation de, à.
Modèle de poids mouche en Java
La route vers Pythonista
À propos du modèle de visiteur
La route vers Djangoist
[Python] Modifier le contrôle du cache des objets téléchargés sur Cloud Storage
Changez les paramètres régionaux Amazon Linux au Japon à l'aide du fichier lineinfile d'Ansible
[python] Remplacez le nom du fichier image par un numéro de série
Changer la destination de sortie standard en un fichier en Python
Une introduction à l'orientation des objets - changeons l'état interne d'un objet
Je veux changer le drapeau japonais en drapeau des Palaos avec Numpy
Changer le volume de Pepper en fonction de l'environnement environnant (son)
Changer le message affiché lors de la connexion à Raspberry Pi
Changez la destination d'installation lorsque --user est ajouté à pip
Comment utiliser le générateur
Point selon l'image
Changer le thème de Jupyter
Changer le style de matplotlib
Comment changer la version de Python
Comment utiliser le décorateur
Comment augmenter l'axe
Comment démarrer la première projection
Comment modifier le niveau de journalisation d'Azure SDK pour Python
Comment changer la couleur du seul bouton pressé avec Tkinter
Changer l'échelle de l'axe Y de Matplotlib en notation exponentielle (10 Nth power notation)
N'hésitez pas à changer l'étiquette de légende avec Seaborn en python
[Go] Créez une commande CLI pour changer l'extension de l'image
Changez l'invite de bash en une couleur simple pour une visualisation facile
J'ai résumé comment changer les paramètres de démarrage de GRUB et GRUB2
Changer la version active dans Pyenv d'Anaconda en Python ordinaire