Previous, Previous, Here A note on interpreting an article in .net / articles / DesignPatterns / flyweight /).
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')
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