May 5, 2016

[python] metaclass used to create derived type instance.

Reference:
http://stackoverflow.com/a/6581949

Python does the following:


  • Is there a __metaclass__ attribute in Foo?
  • If yes, create in memory a class object (I said a class object, stay with me here), with the name Foo by using what is in __metaclass__.
  • If Python can't find __metaclass__, it will look for a __metaclass__ at the MODULE level, and try to do the same (but only for classes that don't inherit anything, basically old-style classes).
  • Then if it can't find any __metaclass__ at all, it will use the Bar's (the first parent) own metaclass (which might be the default type) to create the class object.
  • Be careful here that the __metaclass__ attribute will not be inherited, the metaclass of the parent (Bar.__class__) will be. 
    • If Bar used a __metaclass__ attribute that created Bar with type() (and not type.__new__()), the subclasses will not inherit that behavior.


"""
Metaclass code explain
"""


class ModelMeta(type):
    def __new__(cls, name, bases, attrs):
        print("in new")
        print(name)  # Test2
        print(bases)  # (<class '__main__.Test'>,)
        print(cls)  # <class '__main__.ModelMeta'>

        """
        If not using type.__new__, derived class won't use
        metaclass to create type instance.
        """
        result = type.__new__(cls, name, bases, attrs)
        # result = type(name, bases, attrs)
        print(dir(result))
        """
            ['__class__', '__delattr__', '__dict__', '__doc__',
            '__format__', '__getattribute__', '__hash__', '__init__', '__metaclass__',
            '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__',
            '__sizeof__', '__str__', '__subclasshook__', '__weakref__']
        """
        print(result.__metaclass__)  # <class '__main__.ModelMeta'>
        return result

    def __call__(cls, *args, **kwargs):
        print("in call")
        print(cls)
        instance = type.__call__(cls, *args, **kwargs)
        return instance


class Test(object):
    __metaclass__ = ModelMeta


class Test2(Test):
    pass


class Test3(Test2):
    pass

if __name__ == "__main__":
    t2 = Test2()  # call ModelMeta. __call__






Another perspect

"""
Metaclass code explain
"""


class ModelMeta(type):
    def __new__(cls, name, bases, attrs):
        print("in new")
        print("name:{0}".format(name))  # Test2
        print("bases:{0}".format(bases))  # (<class '__main__.Test'>,)
        print("cls:{0}".format(cls))  # <class '__main__.ModelMeta'>
        print("attrs:{0}".format(attrs))  # <class '__main__.ModelMeta'>
        # Class Test 將擁有空的bases集合
        parents = [b for b in bases if isinstance(b, ModelMeta)]
        print("parents:{0}".format(parents))
        """
        If not using type.__new__, derived class won't use
        metaclass to create type instance.
        """
        result = type.__new__(cls, name, bases, attrs)
        # there will be no inheritence affect if using simply type constructor
        # result = type(name, bases, attrs)
        print(dir(result))
        """
            ['__class__', '__delattr__', '__dict__', '__doc__',
            '__format__', '__getattribute__', '__hash__', '__init__', '__metaclass__',
            '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__',
            '__sizeof__', '__str__', '__subclasshook__', '__weakref__']
        """
        print(result.__metaclass__)  # <class '__main__.ModelMeta'>
        return result

    def __call__(cls, *args, **kwargs):
        print("in call")
        print(cls)
        instance = type.__call__(cls, *args, **kwargs)
        return instance


class Test(object):
    __metaclass__ = ModelMeta
    aa = 1


class Test2(Test):
    a = 2


class Test3(Test2):
    a = 3

if __name__ == "__main__":
    t2 = Test2()  # call ModelMeta. __call__
    print(t2.a)

No comments:

Post a Comment

Note: Only a member of this blog may post a comment.