Python元类(type()和metaclass)

1. 元类是什么

众所周知,对象由类实例化而来,类是对象的模板,而python一切皆对象,类也是对象,它由元类(type)创建,所以元类是类的类,是类的模板

2. 创建类的另一种方法

一般情况下,我们使用class关键字申明一个类,就像

class Demo:
    def __init__(self,name,age):
        self.name = name
        self.age = age
    
    def output(self):
        print("name is " + str(self.name) + " age is " + str(self.age))

if __name__ == '__main__':
    demo = Demo("Bob",18)
    demo.output()

python 中所有的类都是通过type创建的,所以当我们使用type()函数查看类的类型时会显式<class type>

>>> class Demo:
	pass

>>> type(Demo)
<class 'type'>
>>> demo = Demo()
>>> type(demo)
<class '__main__.Demo'>

通过类实例化出来的对象的类型是<class 类名>,这样也更加验证了所有类都是由元类type实例化而来

通过type创建类

可以看一下type的文档,type可以传入三个参数,object_or_name, bases, dict,当只有一个参数是object时,返回该对象的类型,就是最常使用的这种情况,当传入name, bases, dict参数时,会返回一个类,name是类名,bases是基类元组,dict是类中属性和方法的字典

class type(object):
    """
    type(object_or_name, bases, dict)
    type(object) -> the object's type
    type(name, bases, dict) -> a new type
    """

我们使用type重写一下上面的Demo类

# 模拟__init__()
def __init__(self,name,age):
    self.name = name
    self.age = age

def output(self):
    print("name is " + str(self.name) + " age is " + str(self.age))


class_name = 'Demo'
class_bases = (object,)
class_dict = {
    '__init__':__init__,
    'output': output,
}

# type(name, bases, dict) -> a new type
Demo = type(class_name,class_bases,class_dict)
demo = Demo('Bob',18)
demo.output()  # name is Bob age is 18

实际上,每次用class定义类时,执行的都是type()方法

3. MetaClass

既然所有类都是由type创建的,那我们就可以控制类的创建行为,这就需要使用元类metaclass

  • 元类用来创建类,实质上也是一个类,继承自type
  • __new__是真正的构造函数,用来分配内存空间__new__(cls: type, name: str, bases: Tuple[type, ...], namespace: Dict[str, Any]) -> type
def add(self, value):
    print('add one value')
    self.append(value)


class ListMetaclass(type):
    def __new__(mcs, name, bases, namespace):
        namespace['add'] = add
        return type.__new__(mcs, name, bases, namespace)


class MyList(list, metaclass=ListMetaclass):
    pass


li = MyList()
li.add(1)
print(li)

在用class定义类时,括号中可以指定metaclass,指定后会创建__metaclass__,python在创建类的时候,会先检查有没有__metaclass__,如果有,就会以此方法创建对象,没有就会逐级向上查找父类中有没有该,如果找到当前package中还没有找到,就会使用默认的type创建(调用metaclass.__new__())

值得注意的是,如果我们在做类的定义时,在class声明处传入关键字metaclass=ListMetaclass,那么如果传入的这个metaclass有__call__函数,这个__call__函数将会覆盖掉MyList class的__new__函数。这是为什么呢?请大家回想一下,当我们实例化MyList的时候,用的语句是L1=MyList(),而我们知道,__call__函数的作用是能让类实例化后的对象能够像函数一样被调用。也就是说MyList是ListMetaclass实例化后的对象,而MyList()调用的就是ListMetaclass的__call__函数。另外,值得一提的是,如果class声明处,我们是让MyList继承ListMetaclass,那么ListMetaclass的__call__函数将不会覆盖掉MyList的__new__函数。

元类在一般情景下很少用到,但在像ORM中还是会有应用的,ORM(对象关系映射),ORM看这位大佬的文章谈谈Python中元类Metaclass(二):ORM实践

4. 总结

  • 通过class定义的类其实是通过type()创建的
  • type(object_or_name, bases, dict)
  • 如果想要控制类的创建行为,需要在创建类时指定metaclass,一旦指定了metaclass,就会在class上添加__metaclass__,创建类时会找__metaclass__指向的类,并用这个类创建类,如果找不到,就会调用默认的type()

参考文章

Python中的元类(metaclass)
谈谈Python中元类Metaclass(一):什么是元类
Python之元类

发布了62 篇原创文章 · 获赞 33 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/zjbyough/article/details/95667735