python 中元类的理解和相关概念

基本概念

  1. python中一切皆为对象
  2. type本身即是类。type(type)–>
  3. type即是python的元类(metaclass),一切继承了元类的子类都是元类
  4. type是能够创建类的类:type(classname,inherited class(tuple format),attr(dict format))
    class type(name, bases, dict)
    With one argument, return the type of an object. The return value is a type object and generally the same object as returned by object.class.
    The isinstance() built-in function is recommended for testing the type of an object, because it takes subclasses into account.
    With three arguments, return a new type object. This is essentially a dynamic form of the class statement. The name string is the class name and becomes the name attribute; the bases tuple itemizes the base classes and becomes the bases attribute; and the dict dictionary is the namespace containing definitions for class body and is copied to a standard dictionary to become the dict attribute. For example, the following two statements create identical type objects:
    e.g
class X():
    bar=True
class Y(X):
    car='TR'```
#you can use the type to define the class X and Y
X=type('X',(),{'bar'=True})
Y=type('Y',(X,),{'car':'TR'})

创建自己的元类

一个元类的主要目的是当它被创建时,能够自动的该改变类。
例如:当你希望在当前module 里面你创建的所有类的属性都使用大写。那么你可以创建一个元类来实现这个功能,然后通过这个元类创建当前模块中的所有类。
eg1

def upper_attr(future_class_name,future_class_parents,future_class_attr):
    """
        return a class object ,with the list of its attribute turned into uppercase

    """
    uppercase_attr={}
    for name,val in future_class_attr.items():
        if  name.startswith('__') and name.endswith("__"):
            uppercase_attr[name] = val
        else:
            uppercase_attr[name.upper()] = val
    # create the class creation by type
    return type(future_class_name,future_class_parents,uppercase_attr)


class Foo(metaclass=upper_attr):
    bar='bip'
print(hasattr(Foo,'bar'))  # ourput is false
print(hasattr(Foo,'BAR'))  # output is True

我们可以重新定义上面的函数为一个真正的类:

class UpperAttrMetaclass(type):
    # __new__ is the method called before __init__
    # it's the method that creates the object and returns it
    # while __init__ just initializes the object passed as parameter
    # you rarely use __new__, except when you want to control how the object
    # is created.
    # here the created object is the class, and we want to customize it
    # so we override __new__
    # you can do some stuff in __init__ too if you wish
    # some advanced use involves overriding __call__ as well, but we won't
    # see this

    #here upperattr_metaclass is the extra para for __new__,like self for ordinary method
    
    def __new__(upperattr_metaclass,future_class_name,future_class_parents,future_class_attr):
        uppercase_attr={}
        for name,val in future_class_attr.items():
            if  name.startswith('__') and name.endswith("__"):
                uppercase_attr[name] = val
            else:
                uppercase_attr[name.upper()] = val
        #this usage for type is not oop,just use the parent's magic method
        #return type(future_class_name,future_class_parents,uppercase_attr)
        # this is basic OOP, ,nothing magic in there and to call the parent's __new__
        return type.__new__(upperattr_metaclass,future_class_name,future_class_parents,uppercase_attr)
        # this is to use super to do it claearly
        #return super(UpperAttrMetaclass,upperattr_metaclass).__new__(upperattr_metaclass,future_class_name,future_class_parents,uppercase_attr)
class Foo1(metaclass=UpperAttrMetaclass):
    bar='bip'
print(hasattr(Foo1,'bar'))  # ourput is false
print(hasattr(Foo1,'BAR'))  # output is True
print(Foo1.__mro__)
print(Foo1.__dict__)
print(Foo1())

在这里我们使用的参数名字都比较长,为了方便起见,可以使用下面格式

class UpperAttrMetaclass(type):

    def __new__(cls, clsname, bases, dct):

        uppercase_attr = {}
        for name, val in dct.items():
            if not name.startswith('__'):
                uppercase_attr[name.upper()] = val
            else:
                uppercase_attr[name] = val

        return type.__new__(cls, clsname, bases, uppercase_attr)
class UpperAttrMetaclass(type):

    def __new__(cls, clsname, bases, dct):

        uppercase_attr = {}
        for name, val in dct.items():
            if not name.startswith('__'):
                uppercase_attr[name.upper()] = val
            else:
                uppercase_attr[name] = val

        return super(UpperAttrMetaclass, cls).__new__(cls, clsname, bases, uppercase_attr)

这里的例子,我们是在元类中通过__new__来创建新的需要产生的类,并实现了一些我们想要实现的功能。

为什么需要使用元类来代替某个函数来做上面的事情呢?
There are several reasons to do so:

  • The intention is clear. When you read UpperAttrMetaclass(type), you know what’s going to follow
  • You can use OOP. Metaclass can inherit from metaclass, override parent methods. Metaclasses can even use metaclasses.
  • Subclasses of a class will be instances of its metaclass if you specified a metaclass-class, but not with a metaclass-function.
  • You can structure your code better. You never use metaclasses for something as trivial as the above example. It’s usually for something complicated. Having the ability to make several methods and group them in one class is very useful to make the code easier to read.
  • You can hook on new, init and call. Which will allow you to do different stuff. Even if usually you can do it all in new, some people are just more comfortable using init.
  • These are called metaclasses, damn it! It must mean something!
    这里有个例子使用__call__在元类里面实现一些功能:
    eg2
    这里的元类MetaSingleton仅仅是为了判断当实例是否创建
class MetaSingleton(type):
    instance = None
    def __call__(cls, *args, **kw):
        if cls.instance is None:
            cls.instance = super(MetaSingleton, cls).__call__(*args, **kw)
        print(cls.instance)
        return cls.instance

class Foo(metaclass=MetaSingleton):
    #__metaclass__ = MetaSingleton
    def __init__(self,a=None,b=None):
        self.a=a
        self.b=b
        print(self.a,self.b)
    pass
print("*"*20)
c1 = Foo()
print(c1.a,c1.b)
print("+"*20)
c2 = Foo(2,3)
print(c2.a,c2.b)
c2.a=4
c2.b=5
print(c1.a,c1.b)
print(c2.a,c2.b)

下面的例子通过__init__在元类中工作:
eg3

class MyMeta(type):
    counter = 0
    def __init__(cls, name, bases, dic):
        type.__init__(cls, name, bases, dic)
        cls._order = MyMeta.counter
        MyMeta.counter += 1

print("$$$$$$$$$$$$$$$$$$$$$$$")
class MyType(metaclass=MyMeta):    # Python 3
   
    pass
print(MyMeta.counter)   #output is 1
class MyType1(metaclass=MyMeta):
    pass
print(MyMeta.counter)  #output is 2

对于以上代码理解:

  1. 首先以上的自己创建的metaclass都是继承了type类(type本身就是元类,type的subclass也是元类)
  2. 当使用元类(能够创建类的类(类工厂))创建一个正常类(通过class 关键字的类)时,先要运行type的一些内置函数(new–>init),通过元类创建一个类时,首先运行元类的__new__(如果自定义的元类没有找到,就会向上寻找type的__new__),然后运行元类的__init__(如果没有定义,向上寻找父类即type的__init__).当创建类完成后,通过类创建实例的时候,会运行元类的__call__函数(如果没有,向上查找父类(type))
  3. 上面2)的过程就如同我们创建一个普通类A,然后创建实例时(此时会运行类中的__init__函数),当通过实例进行操作inst_a时(print(inst_a())会运行__call__)

元类中两个特殊的magic函数:
prepare
new

  • prepare 当类被创建的时候提供了即将根据客户需求创建的命名空间映射关系。当调用此函数时,必须返回一个命名空间的实例。如果没有调用此函数,将执行一个正常的dict
  • new 负责对最终的类的创建和修改。

这里列出一个通过元类和描述器来约束类的属性的例子

class ValidateType:
    def __init__(self, type):
        self.name = None  # will be set by metaclass
        self.attr = None  # will be set by metaclass
        self.type = type
    def __get__(self, inst, cls):
        if inst is None:
            return self
        else:
            return inst.__dict__[self.attr]
    def __set__(self, inst, value):
        if not isinstance(value, self.type):
            raise TypeError('%s must be of type(s) %s (got %r)' %
                    (self.name, self.type, value))
        else:
            inst.__dict__[self.attr] = value
class Validator(type):
    def __new__(metacls, cls, bases, clsdict):
        # search clsdict looking for ValidateType descriptors
        for name, attr in clsdict.items():
            if isinstance(attr, ValidateType):
                attr.name = name
                attr.attr = '_' + name
        # create final class and return it
        return super().__new__(metacls, cls, bases, clsdict)
    
class Person(metaclass=Validator):
    weight = ValidateType(int)
    age = ValidateType(int)
    name = ValidateType(str)
p=Person()
p.weight=9
print(p.weight)

参考

https://blog.csdn.net/seinedeparis/article/details/79073415

https://stackoverflow.com/users/9951/e-satis

你可能感兴趣的:(python 中元类的理解和相关概念)