元类

源自一句话:在 Python 中,一切皆对象,而对象都是由类实例化得到的

class OldboyTeacher():
    def __init__(self, name, age ,sex):
        self.name = name
        self.age = age
        self.sex = sex

    def score(self):
        print('%s is scoring' %self.name)

tea1 = OldboyTeacher('egon', 18, 'male')
print(type(tea1))     # <class '__main__.OldboyTeacher'>
print(type(OldboyTeacher))  # <class 'type'>

对象 tea1 是调用 OldboyTeacher 类得到的,如果说一切皆对象,那么 OldboyTeacher 也是一个对象,只要是对象,都是调用一个类实例化得到的,即OldboyTeacher = 元类(...),内置的元类是 type

关系:

  • 调用元类 ---> 自定义的类
  • 调用自定义的类 ---> 自定义的对象

调用元类实例化得到自定义的类:OldboyTeacher = type(...)

class OldboyTeacher():
    school = 'Oldboy'
    def __init__(self, name, age ,sex):
        self.name = name
        self.age = age
        self.sex = sex

    def score(self):
        print('%s is scoring' %self.name)

print(OldboyTeacher)

自定义类的三个关键组成部分:

  • 类名
  • 类的基类
  • 类的名称空间
'''
class关键字创建自定义类的底层工作原理, 分为四步:
1. 先拿到类名: 'OldboyTeacher'
2. 再拿到类的基类: (object, )
3. 然后拿到类的名称空间
    怎么拿??? (执行类体代码, 将产生的名字放到类的名称空间, 也就是一个字典里)
    (而类体代码都是一堆字符, 所以要exec函数执行这堆字符, 将执行结果放入字典)
4. 调用元类实例化得到自定义的类: OldboyTeacher = type('OldboyTeacher', (object, ...), {...})

'''

不依赖 class 关键字创建一个自定义类

# 1. 拿到类名
class_name = 'OldboyTeacher'
# 2. 拿到类的基类
class_bases = (object, )
# 3. 拿到类的名称空间
class_dic = {}
class_body = '''
school = 'Oldboy'

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

def score(self):
    print('%s is scoring' %self.name)
'''

exec(class_body, {}, class_dic)
print(class_dic)
# 4. 调用type实例化得到自定义的类
OldboyTeacher = type(class_name, class_bases, class_dic)
print(OldboyTeacher)
# print(OldboyTeacher.__dict__)

tea1 = OldboyTeacher("egon", 18, "male")
print(tea1.__dict__)

自定义元类来控制类的产生

  • 但凡继承了 type 的类,才能称之为自定义的元类,否则就只是一个普通的类
# 模版
class Mymeta(type):
    def __init__(self, class_name, class_bases, class_dic):
        print(self)
        print(class_name)
        print(class_bases)
        print(class_dic)

class OldboyTeacher(object, metaclass=Mymeta): # OldboyTeacher = Mymeta('OldboyTeacher', (object, ), {...})
    school = 'Oldboy'
    def __init__(self, name, age ,sex):
        self.name = name
        self.age = age
        self.sex = sex

    def score(self):
        print('%s is scoring' %self.name)
  • 控制类的产生
# 1. 控制类名必须用驼峰命名法
class Mymeta(type): # 但凡继承了type的类, 才能称之为自定义的元类, 否则就只是一个普通的类
    def __init__(self, class_name, class_bases, class_dic):
        if class_name.islower():
            raise TypeError('类名必须使用驼峰命名法')

class OldboyTeacher(object, metaclass=Mymeta): # OldboyTeacher = Mymeta('OldboyTeacher', (object, ), {...})
    school = 'Oldboy'
    def __init__(self, name, age ,sex):
        self.name = name
        self.age = age
        self.sex = sex

    def score(self):
        print('%s is scoring' %self.name)


# 2. 控制类体必须有文档注释, 且文档注释不能为空
class Mymeta(type): # 但凡继承了type的类, 才能称之为自定义的元类, 否则就只是一个普通的类
    def __init__(self, class_name, class_bases, class_dic):
        if class_name.islower():
            raise TypeError('类名必须使用驼峰命名法')

        doc = class_dic.get('__doc__')
        if doc is None or len(doc) == 0 or len(doc.strip("
 ")) == 0:
            raise TypeError('类体中必须有文档注释, 且文档注释不能为空')

class OldboyTeacher(object, metaclass=Mymeta): # OldboyTeacher = Mymeta('OldboyTeacher', (object, ), {...})
    '''这是注释'''
    school = 'Oldboy'
    def __init__(self, name, age ,sex):
        self.name = name
        self.age = age
        self.sex = sex

    def score(self):
        print('%s is scoring' %self.name)

print(OldboyTeacher.__dict__)

自定义元类来控制类的调用过程

class Mymeta(type): # 但凡继承了type的类, 才能称之为自定义的元类, 否则就只是一个普通的类
    pass

class OldboyTeacher(object, metaclass=Mymeta): # OldboyTeacher = Mymeta('OldboyTeacher', (object, ), {...})
    school = 'Oldboy'

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

    def __call__(self, *args, **kwargs):
        print(self)
        print(args)
        print(kwargs)

    def score(self):
        print('%s is scoring' %self.name)
      
tea1 = OldboyTeacher("egon", 18, "male")
tea1(1,2,a=1,b=2)   # 对象之所以能调用, 是因为对象所在的类有一个__call__函数
  • 推导:如果一切皆对象,那么 OldboyTeacher 也是一个对象,该对象之所以能调用,肯定是这个对象的类中也有一个 __call__ 函数
class Mymeta(type): # 但凡继承了type的类, 才能称之为自定义的元类, 否则就只是一个普通的类
    def __call__(self, *args, **kwargs):
        print(self)
        print(args)
        print(kwargs)

class OldboyTeacher(object, metaclass=Mymeta): # OldboyTeacher = Mymeta('OldboyTeacher', (object, ), {...})
    school = 'Oldboy'

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

    def score(self):
        print('%s is scoring' %self.name)

tea1 = OldboyTeacher("egon", 18, "male")    # 会触发OldboyTeacher的类, 即元类中的__call__函数
  • 调用类实例化会:
    • 先产生一个空对象
    • 执行 __init__ 方法,完成对象的初始化属性操作
    • 返回初始化后的那个对象
  • 调用 OldboyTeacher 就是在调用元类中的 __call__ 函数, 所以这三件事是在调用 __call__ 中执行
class Mymeta(type): # 但凡继承了type的类, 才能称之为自定义的元类, 否则就只是一个普通的类
    def __call__(self, *args, **kwargs):    # self=OldboyTeacher这个类, args=("egon", 18, "male"), kwargs={}
        # 1. 先产生一个空对象
        tea_obj = self.__new__(self)  # 将OldboyTeacher这个类传入, __new__会产生这个类的对象
        # 所以tea_obj是OldboyTeacher这个类的一个对象, 目前是个空对象

        # 2. 执行__init__方法, 完成对象的初始化属性操作
        # 初始化还是到OldboyTeacher这个类中找__init__方法, 按参数一个个传值
        # __init__中的self是对象, 在这里就是tea_obj
        self.__init__(tea_obj, *args, **kwargs)

        # 3. 返回初始化后的那个对象
        return tea_obj

class OldboyTeacher(object, metaclass=Mymeta): # OldboyTeacher = Mymeta('OldboyTeacher', (object, ), {...})
    school = 'Oldboy'

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

    def score(self):
        print('%s is scoring' %self.name)

tea1 = OldboyTeacher("egon", 18, "male")    # 会触发OldboyTeacher的类, 即元类中的__call__函数
print(tea1)
print(tea1.__dict__)
  • 练习:自定义元类来控制类的调用,把属性变为私有
class Mymeta(type): # 但凡继承了type的类, 才能称之为自定义的元类, 否则就只是一个普通的类
    def __call__(self, *args, **kwargs):    # self=OldboyTeacher这个类, args=("egon", 18, "male"), kwargs={}
        tea_obj = self.__new__(self)  # 将OldboyTeacher这个类传入, __new__会产生这个类的对象
        self.__init__(tea_obj, *args, **kwargs)
        # print(tea_obj.__dict__)
        tea_obj.__dict__ = {('_%s__%s' %(self.__name__, k)):v for k,v in tea_obj.__dict__.items()}
        return tea_obj

class OldboyTeacher(object, metaclass=Mymeta): # OldboyTeacher = Mymeta('OldboyTeacher', (object, ), {...})
    school = 'Oldboy'

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

    def score(self):
        print('%s is scoring' %self.name)

tea1 = OldboyTeacher("egon", 18, "male")    # 会触发OldboyTeacher的类, 即元类中的__call__函数
# print(tea1)
print(tea1.__dict__)

属性查找

class Mymeta(type):

    n = 444

    def __call__(self, *args, **kwargs):    # self=OldboyTeacher这个类
        # 推荐使用这种
        tea_obj = self.__new__(self)
        # print(self.__new__ is object.__new__)

        # tea_obj = object.__new__(self)

        self.__init__(tea_obj, *args, **kwargs)
        return tea_obj

class Bar:
    # n = 333
    pass

class Foo(Bar):
    # n = 222
    pass


class OldboyTeacher(Foo, metaclass=Mymeta): # OldboyTeacher = Mymeta('OldboyTeacher', (object, ), {...})
    # n = 111
    school = 'Oldboy'

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

    def score(self):
        print('%s is scoring' %self.name)

    def __new__(cls, *args, **kwargs):
        # print("====>")
        return super().__new__(cls)

tea1 = OldboyTeacher("egon", 18, "male")    # 会触发OldboyTeacher的类, 即元类中的__call__函数

# print(OldboyTeacher.n)
# 对象自己 --> Foo --> Bar --> object --> 元类 -- > type

# print(object.__new__)
原文地址:https://www.cnblogs.com/qiuxirufeng/p/10381788.html