Change the Flyweight pattern to Pythonic (?) (3)

Previous, Previous, Here A note on interpreting an article in .net / articles / DesignPatterns / flyweight /).

Flyweight pattern using metaclass (subclass of type class)

Before the method using the metaclass, I will touch on the prerequisite type class. The type class is used when creating a class definition (= class object).

python


def methodA(self):
    print('methodA')

#Class name, parent class(Multiple specifications are possible), Namespace(Expressed by a dictionary, here'methodA'The methodA function is linked to)Takes as an argument
#Create a class object
ClassA = type('ClassA', (object,), {'methodA': methodA})
a = ClassA()
a.methodA()

#Equivalent to the definition below
# class ClassA(object):
#     def methodA(self):
#        print('methodA')

As mentioned above, type is a class. Therefore, subclasses can be created. Subclasses can be used to create class objects instead of the type class, and you can add processing when creating class objects by overriding the __new__ and __init __ methods.

python


def methodA(self):
    print('methodA')

class Meta(type):
    def __new__(cls, name, bases, namespace):
        #You can insert additional processing when creating a class object
        print('hoge')
        return super().__new__(cls, name, bases, namespace)

#Meta class instead of type class(=A subclass of the type class)Can be used
ClassA = Meta('ClassA', (object,), {'methodA': methodA})
a = ClassA()
a.methodA()

If you want to use something other than the type class when creating a class object in normal class definition syntax, use the metaclass keyword in Python3. The class specified at this time is called a metaclass.

python


class Meta(type):
    def __new__(cls, name, bases, namespace):
        #You can insert additional processing when creating a class object
        print('hoge')
        return super().__new__(cls, name, bases, namespace)

class ClassA(object, metaclass=Meta):
#Specifying the Python2 metaclass
#class ClassA(object):
#    __metaclass__ =Meta
    def methodA(self):
        print('methodA')

a = ClassA()
a.methodA()

The following is an example of inserting the logic of the Flyweight pattern when creating a class object by using the property of the metaclass that "processing can be added when creating a class object".

python


class MetaFlyweight(type):
    def __new__(cls, name, bases, namespace):
        #type class__new__Method returns class object
        clsobj = type.__new__(cls, name, bases, namespace)

        clsobj._instances = {}
        def _get_instance(cls, *args, **kargs):
            instance = super(cls, cls).__new__(cls)
            return cls._instances.setdefault(
                    (args, tuple(kargs.items())), instance)
        #It works without making it a static method, but make it a static method according to the following description
        # (ref.) https://docs.python.org/3.3/reference/datamodel.html#object.__new__
        clsobj.__new__ = staticmethod(_get_instance)
        return clsobj

class Hoge(object, metaclass=MetaFlyweight):
#Specifying the Python2 metaclass
#class Hoge(object):
#    __metaclass__ = MetaFlyweight
    def __init__(self, args1, kwargs1='test1'):
        self.args1 = args1
        self.kwargs1 = kwargs1

class Piyo(object, metaclass=MetaFlyweight):
#Specifying the Python2 metaclass
#class Piyo(object):
#    __metaclass__ = MetaFlyweight
    def __init__(self, args1, kwargs1):
        self.args1 = args1
        self.kwargs1 = kwargs1

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') is Piyo('a', kwargs1='b')
assert Piyo('a', kwargs1='b') is not Piyo('a', kwargs1='c')
assert Hoge('a', kwargs1='b') is not Piyo('a', kwargs1='b')
assert Hoge('a', kwargs1='b') is not Piyo('a', kwargs1='b')

Flyweight pattern (function) using metaclass

However, it is not always necessary to generate a subclass of the type class. When creating a class object, the type class given the "class name (character string)", "parent class (tuple)", and "namespace (dictionary)" is called. However, what is needed here is not a type class (or subclass), but a" callable object that accepts the above three arguments ". Therefore, it is also possible to specify the function in the metaclass as follows.

python


def meta_flyweight(name, bases, namespace):
    clsobj = type(name, bases, namespace)

    clsobj._instances = {}
    def _get_instance(cls, *args, **kargs):
        instance = super(cls, cls).__new__(cls)
        return cls._instances.setdefault(
                (args, tuple(kargs.items())), instance)
    #It works without making it a static method, but make it a static method according to the following description
    # (ref.) https://docs.python.org/3.3/reference/datamodel.html#object.__new__
    clsobj.__new__ = staticmethod(_get_instance)
    return clsobj

class Hoge(object, metaclass=meta_flyweight):
#Specifying the Python2 metaclass
#class Hoge(object):
#    __metaclass__ = meta_flyweight
    def __init__(self, args1, kwargs1='test1'):
        self.args1 = args1
        self.kwargs1 = kwargs1

class Piyo(object, metaclass=meta_flyweight):
#Specifying the Python2 metaclass
#class Piyo(object):
#    __metaclass__ = meta_flyweight
    def __init__(self, args1, kwargs1):
        self.args1 = args1
        self.kwargs1 = kwargs1

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') is Piyo('a', kwargs1='b')
assert Piyo('a', kwargs1='b') is not Piyo('a', kwargs1='c')
assert Hoge('a', kwargs1='b') is not Piyo('a', kwargs1='b')
assert Hoge('a', kwargs1='b') is not Piyo('a', kwargs1='b')

Recommended Posts

Change the Flyweight pattern to Pythonic (?) (3)
Change the Flyweight pattern to Pythonic (?) (2)
Change the Flyweight pattern to Pythonic (?) (1)
[Python] Change the alphabet to numbers
Learn the design pattern "Flyweight" in Python
Script to change the description of fasta
[Python] How to change the date format (display format)
Change the decimal point of logging from, to.
Flyweight pattern in Java
The road to Pythonista
About the Visitor pattern
The road to Djangoist
[Python] Change the Cache-Control of the object uploaded to Cloud Storage
Change the Amazon Linux locale to Japan using Ansible's lineinfile
[python] Change the image file name to a serial number
Change the standard output destination to a file in Python
An introduction to object orientation-let's change the internal state of an object
I want to change the Japanese flag to the Palau flag with Numpy
Change the volume of Pepper according to the surrounding environment (sound)
Change the message displayed when logging in to Raspberry Pi
Change the installation destination when --user is added to pip
How to use the generator
How to change Jupyter layout
Dot according to the image
The road to download Matplotlib
Change the theme of Jupyter
Change the style of matplotlib
How to change Python version
How to use the decorator
How to increase the axis
How to start the program
How to change the log level of Azure SDK for Python
How to change the color of just the button pressed in Tkinter
Change the Y-axis scale of Matplotlib to exponential notation (10 Nth power notation)
Feel free to change the label of the legend in Seaborn in python
[Go] Create a CLI command to change the extension of the image
Change the bash prompt to a simple color for easy viewing
I summarized how to change the boot parameters of GRUB and GRUB2
Change the active version in Pyenv from anaconda to plain Python