096 元类

一、什么是元类

  • 在python里面,我们都知道一切都是对象
  • 我们用calss关键字定义一个类出来,那这个类肯定也就是一个对象。那这个类肯定又是由另一个类实例化的来的,负责产生这个对象的类就称之为是元类
  • 产生类的类叫做元类
  • 元类:简称为类的类

二、如何去找元类

  1. type是一个内置的元类,所有的类都是由type实例化得到的,产生类的类,叫元类
  2. type的实例就是type,并且它还继承了object类,而object类也是type类的实例。
class Person:
    def __init__(self,name):
        self.name=name
    def score(self):
        print('分数是100')

p=Person('nick')

a=Person
p=a('xichen')
print(p.name)
print(type(p))

<class ‘main.Person‘>

print(type(Person))
print(type(dict))
print(type(list))
print(type(str))
print(type(object))

print(type(type))

<class ‘type‘>
<class ‘type‘>
<class ‘type‘>
<class ‘type‘>
<class ‘type‘>
<class ‘type‘>

在这里我们发现,我们打印出来的结果都显示type是产生dict、list、str、的元类

**由此可以知道type是所有类的元类,并且type是继承object的类

2.1元类的继承关系图

三、class底层原理分析

  1. class 类名,会把类构造出来
  2. 实际上是:class 底层就是调用type来实例化产生类(对象),需要传一堆参数。只不过class关键字帮我们干了这个事情
  3. type(object_or_name, bases, dict)

    object_or_name:类的名字,是个字符串
    bases:是它的所有父类,基类
    dict:名称空间,是一个字典

3.1通过type来直接产生类,不用class关键字了

def __init__(self,name)
    self.name = name

Person = type('Person',(object,),{'age':'18','__init__':__init__})
print(Person.__dict__)

{‘school‘: ‘oldboy‘, ‘init‘: <function init at 0x00000273833A1E18>, ‘module‘: ‘main‘, ‘dict‘: <attribute ‘dict‘ of ‘Person‘ objects>, ‘weakref‘: <attribute ‘weakref‘ of ‘Person‘ objects>, ‘doc‘: None}
(<class ‘object‘>,)

在这里我们可以看到我们打印Person这个类的父类的时候,我们发现它是继承的是object类,虽然它是用type元类实例化出来的类,这就进一步说明了type在实例化类对象的时候,其实是继承object的原理

3.2通过exec内置函数和type创建类,不用class关键字了

l={}
exec('''
school='oldboy'
def __init__(self,name):
    self.name=name
def score(self):
    print('分数是100')
''',{},l)
def __init__(self,name):
    self.name=name

Person=type('Person',(object,),l)

print(Person.__dict__)

{‘school‘: ‘oldboy‘, ‘init‘: <function init at 0x000001B15A301E18>, ‘score‘: <function score at 0x000001B15C175620>, ‘module‘: ‘main‘, ‘dict‘: <attribute ‘dict‘ of ‘Person‘ objects>, ‘weakref‘: <attribute ‘weakref‘ of ‘Person‘ objects>, ‘doc‘: None}

print(Person.__bases__)

(<class ‘object‘>,)

p=Person('xichen')
print(p.name)

xichen

print(p.__dict__)

{‘name‘: ‘xichen‘}

四、通过元类来控制类的产生

自定义元类控制类的产生:可以控制类名,可以控制类的继承父类,控制类的名称空间

自定义元类必须继承type,如果一个类继承type, 这种类都叫元类

先写元类,在通过元类去控制类的产生,要在类继承的括号中写上metaclass=元类名表示使用的元类

class Mymeta(type):
    # def __init__(self,*args,**kwargs):    # 正常写法,但是我们已经知道type传递的三个参数的意义
    def __init__(self,name,bases,dic):
        # self 就是Person类
        print(name)     # 创建的类名
        print(bases)    # 继承的所有父类
        print(dic)      # 类中属性,是一个字典

        #加限制 控制类名:必须以sb开头
        if not name.startswith('sb'):
            raise Exception('类名没有以sb开头')

        #类必须加注释
        print(self.__dict__['__doc__'])
        doc=self.__dict__['__doc__']
        if not doc:
            #没有加注释
            raise Exception('你的类没有加注释')
#metaclass=Mymeta  指定这个类生成的时候,用自己写的Mymeta这个元类
class Person(object,metaclass=Mymeta):
    age='18'
    def __init__(self,name):
        self.name=name
    def score(self):
        print('分数是100')
p=Person("xichen")

Person

(<class ‘object‘>,)

{‘module‘: ‘main‘, ‘qualname‘: ‘Person‘, ‘doc‘: ‘\n 注释\n ‘, ‘age‘: ‘18‘, ‘init‘: <function Person.__init__ at 0x000001B97FDF19D8>, ‘score‘: <function Person.score at 0x000001B97FDF1A60>}

Exception: 类名没有以sb开头

五、通过自定义元类控制类的调用过程

控制类的调用过程,实际上在控制:对象的产生 __call__

之前的内容说过,__call__当对象加括号就会调用它。那么现在放到类对象也同样适用。

但只是说过类名()实例化会先去调用init绑定方法,现在又说call是不是很混乱。

实际上,类名()实例化是先去调用call方法,再去调用init方法

如:

class Mymeta(type):
    def __call__(self, *args, **kwargs):
        print('xxx')
        return 1

class Person(object,metaclass=Mymeta):
    age='18'
    def __init__(self,name):
        self.name=name
    def score(self):
        print('分数是100')

#先触发元类的__call__,再触发init的执行
p=Person('xichen')    # 当类对象加括号实际上就是去调用了自定义元类中的__call__方法
print(p)   # 所以p等于1
# print(p.name)   # 没有返回对象,p等于1所以一定会报错

那么,如果像上面那样,不就得不到实例化对象了嘛?

实际上,实例化创建对象是通过call方法中的new方法。这些call方法,new方法,init都是一些魔法方法

但是new方法只会得到一个新的对象什么都没有的对象

但我们实例化得到对象的本事是为了共享类中属性和方法,需要绑定上属性和方法。这时候就需要再使用init方法

如:

class Mymeta(type):
    def __call__(self, *args, **kwargs):
        # self 就是Person类
        print(2)
        print(self.mro()) # 类对象的属性查找顺序
        # obj=self.__new__(self)  # 方法1,通过self的new方法创建对象
        obj=object.__new__(self)  # 方法2,通过object的new方法创建对象
        obj.__init__(*args, **kwargs)   # 调用对象的init方法
        return obj

    def __init__(self,*args):
        print(3)
        print(args)

class Person(metaclass=Mymeta): # 实际上是先调用元类的call方法,再回调自定义元类的init方法
    age = '18'
    def __init__(self, name):   # 当类实例化生成对象后,会通过call调用init方法
        print(1)
        self.name = name
p = Person(name='xichen') # 先调用自定义元类的call方法
print(p.__dict__)
print(p.name)
print(p)

3
(‘Person‘, (), {‘module‘: ‘main‘, ‘qualname‘: ‘Person‘, ‘age‘: ‘18‘, ‘init‘: <function Person.__init__ at 0x0000012BFC236A60>})
2
[<class ‘main.Person‘>, <class ‘object‘>]
1
{‘name‘: ‘xichen‘}
xichen
<main.Person object at 0x0000012BF53229B0>

六、有了元类之后的属性查找顺序

  • 类的属性查找顺序:先从类本身中找--->mro继承关系去父类中找---->去自己定义的元类中找--->type中--->报错
  • 对象的属性查找顺序:先从对象自身找--->类中找--->mro继承关系去父类中找--->报错

原文地址:https://www.cnblogs.com/xichenHome/p/11456335.html

时间: 2024-11-06 09:28:31

096 元类的相关文章

深刻理解Python中的元类(metaclass)

译注:这是一篇在Stack overflow上很热的帖子.提问者自称已经掌握了有关Python OOP编程中的各种概念,但始终觉得元类(metaclass)难以理解.他知道这肯定和自省有关,但仍然觉得不太明白,希望大家可以给出一些实际的例子和代码片段以帮助理解,以及在什么情况下需要进行元编程.于是e-satis同学给出了神一般的回复,该回复获得了985点的赞同点数,更有人评论说这段回复应该加入到Python的官方文档中去.而e-satis同学本人在Stack Overflow中的声望积分也高达6

【原创】Python 对象创建过程中元类, __new__, __call__, __init__ 的处理

原始type: type是最原始的元类,其__call__方法是在你使用" t_class = type(classname_string, base_classes_tuple, attributes_dict)" 这种语法来使用时, 在__call__方法内使用又会调用type的__new__和__init__方法来创建classname_string的具体类,并初始化类信息.当type(***)调用完成, classname_string代表的类可以用来创建实例了. 元类调用过程

QObject提供了QMetaObject元类信息(相当于RTTI和反射),信号与连接,父子关系,调试信息,属性,事件,继承关系,窗口类型,线程属性,时间器,对象名称,国际化

元类信息(相当于RTTI和反射),信号与连接,父子关系,调试信息,属性,事件,继承关系,窗口类型,线程属性,时间器,对象名称,国际化其中元类又提供了:classInfo,className,构造函数,多重祖先元类,method, property, Enumerator, Signal, Slot等等 http://doc.qt.io/qt-5/qobject.html http://doc.qt.io/qt-5/qmetaobject.html 我感觉Qt的出现,除了提供GUI以外,主要就是提

Python全栈开发之9、面向对象、元类以及单例

前面一系列博文讲解的都是面向过程的编程,如今是时候来一波面向对象的讲解了 一.简介 面向对象编程是一种编程方式,使用 “类” 和 “对象” 来实现,所以,面向对象编程其实就是对 “类” 和 “对象” 的使用.类就是一个模板,模板里可以包含多个方法(函数),方法里实现各种各样的功能,,对象则是根据模板创建的实例,通过实例,对象可以执行类中的方法,每个对象都拥有相同的方法,但各自的数据可能不同. 二.类.对象和方法 在Python中,定义类是通过class关键字,class后面紧接着是类名,类名通常

Objective-C对象之类对象和元类对象

作者:wangzz 原文地址:http://blog.csdn.net/wzzvictory/article/details/8592492 转载请注明出处 如果觉得文章对你有所帮助,请通过留言或关注微信公众帐号wangzzstrive来支持我,谢谢! 作为C语言的超集,面向对象成为Objective-C与C语言的最大区别,因此,对象是Objective-C中最重要的部分之一.目前面向对象的语言有很多,Objective-C中的对象又和其他语言中的对象有什么区别呢?下面来简单介绍Objectiv

.NET 中,编译器直接支持的数据类型称为基元类型(primitive type).基元类型和.NET框架类型(FCL)中的类型有直接的映射关系.

.NET 中,编译器直接支持的数据类型称为基元类型(primitive type).基元类型和.NET框架类型(FCL)中的类型有直接的映射关系. The primitive types are Boolean, Byte, SByte, Int16, UInt16, Int32, UInt32, Int64, UInt64, IntPtr, UIntPtr, Char, Double, and Single. https://msdn.microsoft.com/zh-cn/library/s

MyPython--&gt;进阶篇--&gt;使用枚举类 元类

当我们需要定义常量时,一个办法是用大写变量2通过整数来定义,例如月份 JAN = 1 FEB = 2 MAR = 3 好处是简单,缺点是类型是int,并且仍然是变量 更好的方法是为这样的枚举类型定义一个class类,然后每个常量都是class的一个唯一实例,python提供了Eum类来实现这个功能 from enum import Enum Month = Enum('Month',('Jan','Feb','Mar','Apr','May')) print(Month.May) for nam

Python中元类

一. 前提: python中一切都是对象,要么是类的对象,要么是元类的对象,type元类是自己的对象.继承层次表示为type(元类)-->类(内置和用户自定义的) --> 类的实例. 二.  目的: 创建类时自动改变类,换句话说就是创建类这种东西的东西. 三.  __metaclass__ 可以通过指定__metaclass__来使用用户自定义的元类创建类. class语句解析顺序: class Myclass(object): pass Myclass里面的__metaclass__ --&

Python-深入理解元类(metaclass)

1.使用 type 动态创建类(type 是一个类, 用来创建类对象的元类, 所以也可以继承) type("Person", (), {"name": "John"}) 2.元类 Python 中类也是对象, 元类就是创建这些类对象的类, 可以理解为 MyClass = MetaClass() MyObject = MyClass() 3.type实际上是一个元类, type就是Python在背后用来创建所有类的元类, 类似 str 是创建字符串