Python面向对象之八:元类

Python面向对象之八:元类

一、概述

1 、元类的定义

在python中,对象是由类实例化而来,那么按照万事万物皆对象的理念,类也是对象,那它是怎么来的呢?

答案是:元类,元类就是创建类的类。

在Python当中万物皆对象,我们用class关键字定义的类本身也是一个对象,负责产生该对象的类称之为元类,元类可以简称为类的类,元类的主要目的是为了控制类的创建行为。

2、查看元类的类型
class Person:
    pass

class Mother(Person):
    pass


mother = Mother()
print(type(mother))  #<class '__main__.Mother'>

print(type(Mother))  #<class 'type'>
print(type(Person))  #<class 'type'>

分析:
1、对象的类型是:<class ‘main.Mother’>,说明它是由Mother类实例化而来;
2、类的类型是:<class ‘type’>,说明它是由type这个元类而实例化得来的。

3、关于type

1、type是Python的一个内建元类,用来直接控制生成类,在python当中任何class定义的类其实都是type类实例化的结果。

2、只有继承了type类才能称之为一个元类,否则就是一个普通的自定义类,自定义元类可以控制类的产生过程,类的产生过程其实就是元类的调用过程。

3、Python中所有的东西,注意,我是指所有的东西都是对象。这包括整数、字符串、函数以及类。它们全部都是对象,而且它们都是从一个类创建而来,这个类就是type。

二、类的创建

1、通过class关键字创建
class Person(object):
    country = 'China'
    def __init__(self,name,age):
        self.name = name
        self.age = age

    def tell(self):
        print(f'{self.name}的年龄是:{self.age}')
2、通过type关键字创建
country = 'China'
def __init__(self,name,age):
    self.name = name
    self.age = age

def tell(self):
    print(f'{self.name}的年龄是:{self.age}')

Person = type('Person',(object,),{'country':country,
                                  '__init__':__init__,
                                  'tell':tell})

print(type(Person))   #<class 'type'>
per = Person('晴朗', 18)
print(type(per))    #<class '__main__.Person'>

分析:Person类实际上就是一个类对象,于是我们将产生类的类称之为元类,默认的元类是type。

3、自定义元类

一个类没有声明自己的元类,默认他的元类就是type,除了使用内置元类type,我们也可以通过继承type来自定义元类,然后使用metaclass关键字参数为一个类指定元类:

class MyMetaClass(type):
    pass

class Person(object, metaclass=MyMetaClass):
    country = 'China'
    def __init__(self,name,age):
        self.name = name
        self.age = age

    def tell(self):
        print(f'{self.name}的年龄是:{self.age}')

print(type(Person))   #<class '__main__.MyMetaClass'>
per = Person('晴朗', 18)
print(type(per))    #<class '__main__.Person'>
print(per.__dict__)  #{'name': '晴朗', 'age': 18}

分析:自定义元类可以控制类的产生过程,类的产生过程其实就是元类的调用过程,即:

class MyMetaClass(type):
    pass

country = 'China'
def __init__(self,name,age):
    self.name = name
    self.age = age

def tell(self):
    print(f'{self.name}的年龄是:{self.age}')

Person = MyMetaClass('Person',(object,),{'country':country,
                                  '__init__':__init__,
                                  'tell':tell})

print(type(Person))   #<class 'type'>
per = Person('晴朗', 18)
print(type(per))    #<class '__main__.Person'>
print(per.__dict__)  #{'name': '晴朗', 'age': 18}

三、元类的使用

1、控制类的创建
class Mymeta(type):  # 只有继承了type类的类才是自定义的元类
    def __init__(self, class_name, class_bases, class_dic):
        print(self)  
        print(class_name)
        print(class_bases)
        print(class_dic)

        # 产生的新类必须至少有一个父类
        if len(class_bases) == 0:
            raise BaseException("至少继承一个父类")

        # 产生的新类必须有注释文档
        doc = class_dic.get('__doc__')
        if not (doc and len(doc.strip()) > 0):
            raise BaseException("必须要有文件注释,并且注释内容不为空")

class Person(object, metaclass=Mymeta):

    '''
    这里是注释文档
    '''
    pass
2、控制类的实例化
2.1、_ call _方法
class Foo:
    def __call__(self, *args, **kwargs):
        print(self)
        print(args)
        print(kwargs)

obj = Foo()

res = obj(1, 2, 3, x=1, y=2) #只有写了这行代码,__call__才会运行

分析:调用一个对象去执行,就是触发对象所在类中的__call__方法的执行。

2.2 _ new _方法
class Student(object):
    def __new__(cls, *args, **kwargs):
        print('---------new--------------')
        obj = super().__new__(cls)
        return obj

    def __init__(self, name, age):
        print('---------init--------------')
        self.name = name
        self.age = age

student = Student('wtt', 30)
#运行结果:
# ---------new--------------
# ---------init--------------

分析:创建一个对象,首先会运行__new__方法创建一个空对象,然后再运行__init__方法装备名称空间。

2.3控制类的实例化

1、实例化一个对象会做三件事:产生一个空对象obj、调用__init__方法初始化对象obj、返回初始化好的obj,对应的__call__方法也会做这三件事:
在这里插入图片描述

分析:__call__方法相当于一个模板,我们可以在该基础上改写__call__的逻辑从而控制调用类的过程

2、利用改写__call__方法将类模板中用于实例化的属性全部变成私有属性:

class Person(type):

    def __call__(self, *args, **kwargs):
        # 1、先创建一个空对象
        obj = object.__new__(self)
        # 2、调用类内的__init__函数,然后将空对象连同括号内的参数的参数一同传给__init__
        self.__init__(obj, *args, **kwargs)
        obj.__dict__ = {"_%s__%s" % (self.__name__, k): v for k, v in obj.__dict__.items()}
        # 3、将初始化好的对象返回
        return obj

class Teacther(object, metaclass=Person):

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

    def sya(self):
        print(f'{self.name}说了句话!')

t1 = Teacther('晴朗', 20)
print(t1.__dict__)
#{'_Teacther__name': '晴朗', '_Teacther__age': 20}

四、元类的冲突

1、假如有两个不同的元类,要生成一个继承这两个类的子类,会产生什么情况呢?

class MetaA(type):
    pass

class MetaB(type):
    pass

class A(object, metaclass=MetaA):
    pass

class B(object, metaclass=MetaB):
    pass

class C(A, B):
    pass

#TypeError: metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases

分析:这时会报错,派生类的元类必须是其所有基元类的子类,而类C来自于两个继承不同基元类的子类,所以会元类冲突。

2、修改一下以上代码,保证派生类的元类是其所有基元类的子类

class MetaA(type):
    pass

class MetaB(type):
    pass

class MetaC(MetaA, MetaB):    #保证派生类的元类是其所有基元类的子类
    pass

class A(object, metaclass=MetaA):
    pass

class B(object, metaclass=MetaB):
    pass

class C(object, metaclass=MetaC):
    pass
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值