python 吐血总结面向对象元类

Posted jackson669

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了python 吐血总结面向对象元类相关的知识,希望对你有一定的参考价值。

什么是元类

元类是python面向对象编程的深层魔法,很多人都不得要领。

在python中一切皆是对象,用class定义得类本身也是一个对象,负责产生该对象的类称之为元类,即元类可以简称为类的类。

 

简单来说,只要继承了type,他就是元类

# Person也是一个对象,那么他一定是由一个类实例化得到的,这个类也就是元类
class Person:
    pass
p1 = Person
# type类 是产生所有类的元类
print(type(Person)) print(type(list)) print(type(dict)) print(type(object)) # <class ‘type‘>

为什么用到元类

元类是负责产生类的,所以我们学习元类或者自定义元类的目的,是1、为了控制类的产生过程,2、还可以控制对象的产生过程。

class创建类

class People:            # People = type()
    country = China

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

    def eat(self):
        print(%s is eating % self.name)
class加类名,就会把类构造出来

用class关键字创建一个类,用默认的元类type。

技术图片

type元类

 

Person类是有type实例化产生的,向type传了一堆参数,然后type()类调用__init__方法,就会创建一个类。

type()创建类格式:

type(object, bases(继承的基类),  dict)

object: object(所有基类) or name (类名:你需要创建的名字),类的名字,是个字符串

base:是他的所有的父类 (元组的形式)、基类

dict:名称空间,是一个字典;
创建类的三要素:类名、基类、类的名称空间

 

通过type 直接产生的类,不用class关键字
def __init__(self, name, age):
    self.name = name
    self.age = age


# 名称空间是一个字典
# 创建类 People = type(类名基类类的名称空间)
Person = type(Person, (object, ), {school: hnnu,__init__: __init__,sex:boy})

# 创建对象
p = Person(randy,12)
print(p.say(p))

通过元类来控制类的产生

# 自定义元类马? ilil

# Mymeta就是一个元类,因为它继承了type
class Mymeta(type): def __init__(self, name, base, dict): print(name) # 类名 print(base) # 基类 print(dict) # 名称空间 """ 通过class 创建的类继承,自定义的元类,在Person中定义第一个参数表示类的名称,第二个参数表示继承的基类,而类中的代码dict则是,产生的名称空间。 """ class Ren(object, metaclass=Mymeta): school = hlxy def __init__(self, name): self.name = name def score(self): print(score is 100) p = Ren(sb

例子1

class Mymeta(type):
    def __init__(self, name, base, dic):
        # 练习一 加限制, 限制类名必须以sb开头
        print(name)
        if not name.startswith(heihei):
            raise Exception(类名没有以heihei开头!!!!)

class heihei_Ren(object, metaclass=Mymeta):
    school = hei
    def __init__(self, name):
        self.name = name
        print(2)

    def score(self):
        print(分数为10000)

p1 = heihei_Ren(Jac)

例子2

class Mymeta(type):

    def __init__(self, name, base, dic):
        print(self.__dict__)  # 产生的名称空间是类
        print(dic)
        doc = self.__dict__[__doc__]
        print("元类")
        if not doc:
            raise Exception("必须有注释")

        print(doc)


class Person(object, metaclass=Mymeta):
    """
    我已经注释了
    """
    school = hnnu

    def __init__(self, name):
        print("自己")
        self.name = name

    def score(self):
        print("分数是: 100")


# 继承元类,首先会先进入元类中的__init__在返回来执行p1实例,然后再执行p1实例里面的__init__
p1 = Person(ran)

通过控制类产生的模板

class Mymeta(type):
    def __init__(self, name, base, dic):
        if name == Person:
            raise Exception("名称错误")


class Person1(metaclass=Mymeta):
    def __init__(self, name, age):
        self.name = name
        self.age = age


p = Person1(laowang, 19)

通过元类控制类得调用过程(控制创建类对象的过程)

 

想要让obj这个对象变成一个可调用的对象,需要在该对象的类中定义一个方法,__call__方法,该方法会在调用对象的时候,自动触发

 

 

我们之前说类实例化第一个调用的是__init__,但__init__其实不是实例化一个类的时候第一个被调用的方法。 

当时候Person(name,age)这样表达式来实例化一个类的时候,最先被调用的方法其实是__new__方法。

 

 

__new__方法接受的参数虽然和__init__一样,但__init__是在实例创建之后调用的,而__new__正是创建实例的方法。

 

把对象属性都变成私有属性

 

class Mymeta(type):
    def __call__(self, *args, **kwargs):
        print("__call__ 第一")
        # 产生空对象
        obj = object.__new__(self)
        # 调用子类__init__
        obj.__init__(*args, **kwargs)

        # 名称空间创建完成之后进行处理
        # 会进入self==> person类中__init__然后在回来
        obj.__dict__ = {f"_{self.__name__}__{k}": v for k, v in obj.__dict__.items()}

        # print("123456", obj.__dict__)
        # print("__call__ ", obj.__dict__)

        return obj


class Person(object, metaclass=Mymeta):
    def __init__(self, name):
        print("___init__ 第二")
        self.name = name

    def score(self):
        print(分数是100)


p = Person(name=randy)
print(p.__dict__)
# print(p._Person__name)

 

自定义元类后继承顺序

 

结合python继承得实现原理+元类重新看属性得查找是什么样子

在学完元类之后,就发现每一个定义的clss都是一个对象(包括object类本身也是元类type得一个实例,可以用type(object))查看,我们也学习过继承的实现原理。
class Mymeta(type):  # 只有继承了type类才能称之为一个元类,否则就是一个普通的自定义类
    n = 444

    def __call__(self, *args,**kwargs):  #self=<class ‘__main__.OldboyTeacher‘>
        obj = self.__new__(self)
        self.__init__(obj, *args, **kwargs)
        return obj


class Bar(object):
    n = 333


class Foo(Bar):
    n = 222


class OldboyTeacher(Foo, metaclass=Mymeta):
    n = 111

    school = oldboy

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

    def say(self):
        print(%s says welcome to the oldboy to learn Python % self.name)


print(
    OldboyTeacher.n
)  # 自下而上依次注释各个类中的n=xxx,然后重新运行程序,发现n的查找顺序为OldboyTeacher->Foo->Bar->object->Mymeta->type

 

查找顺序:

1、先在对象层:Oldboy——Foo——Bar——object

2、然后元类层:Mymeta——type

上述总结: 分析下元类Mymeta中__call__的self.__new__的查找

C3算法

技术图片

总结

继承元类,首先回进入元类中的__init__在返回来执行自己的__init__

控制类产生模板

#  控制类产生模板
class Mymeta(type):
    def __init__(self, name, base, dic):
        if self.name == Person:
            raise Exception("名称错误")


class Person(metaclass=Mymeta):
    def __init__(self, name, age):
        self.name = name
        self.age = age


p = Person(laowang, 19)

控制对象的产生

class Mymeta(type):
    def __call__(self, *args, **kwargs):

        # 第一步产生空对象

        obj=object.__new__(self)
        # 第二部初始化空对象,把初始值放到对象中
        obj.__init__(*args, **kwargs)
        # 第三步返回对象
        return obj

class Person(metaclass=Mymeta):
    def __init__(self,name):
        self.name=name
    def __call__(self, *args, **kwargs):
        print(xxx)

p=Person(randy)

 

以上是关于python 吐血总结面向对象元类的主要内容,如果未能解决你的问题,请参考以下文章

python 面向对象编程 之 元类

Python3 面向对象之-----元类

Python----面向对象---元类介绍

python面向对象--元类

python 面向对象专题:元类type反射函数与类的区别特殊的双下方法

python 面向对象专题:元类type反射函数与类的区别特殊的双下方法