元类

一:元类介绍

“元类就是深度的魔法,99%的用户应该根本不必为此操心。

如果你想搞清楚究竟是否需要用到元类,那么你就不需要它。

那些实际用到元类的人都非常清楚地知道他们需要做什么,而且根本不需要解释为什么要用元类。”

—— Python界的领袖 Tim Peters


一切都源自一句话:在Python中,一切皆对象

元类:类他爹

定义类,可以控制对象的产生

元类,可以控制类的产生

# 元类 ==> People类 ==> obj
class People(object):
    school = 'Tinghua'

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

    def say(self):
        print(f'Hello {self.name}!')


obj = People('xxq', 18)
# 调用People类 ==> 对象obj
# 调用元类 ==> People类

# 查看类(类型)
print(type(obj))  # <class '__main__.People'>
print(type(People))  # <class 'type'>   # 默认的元类是:type


结论:

默认的元类是:type,默认情况下,我们用class关键字定义的类 都是由type产生的

二:class关键字 底层做了哪些事?

1.先拿到1个类名

class_name = 'People'

2.拿到 类的父类

class_bases = (object,)

3.再拿到类的名称空间(运行类体代码得到的)

class_dic = {}
class_body = '''
    school = 'Tinghua'

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

    def say(self):
        print(f'Hello {self.name}!')
'''
exec(class_body, {}, class_dic)
# print(class_dic)

补充:exec的使用

dic = {}    # 空字典
s = '''
a = 1
b = 2
c = 3
'''

exec(s, {}, dic)
print(dic)

4.调用元类(传入类的3大要素:类名、基类、类的名称空间) 得到1个元类的对象,然后将元类的变量赋值给People(这个People就是我们用class自定义的类)

People = type(class_name, class_bases, class_dic)

5.完整思路

# 1.先拿到1个类名
class_name = 'People'

# 2.拿到 类的父类
class_bases = (object,)

# 3.再拿到类的名称空间(运行类体代码得到的)
class_dic = {}
class_body = '''
    school = 'Tinghua'

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

    def say(self):
        print(f'Hello {self.name}!')
'''
exec(class_body, {}, class_dic)
# print(class_dic)

# 4.调用元类(传入类的3大要素:类名、基类、类的名称空间) 得到1个元类的对象,然后将元类的变量赋值给People(这个People就是我们用class自定义的类)
People = type(class_name, class_bases, class_dic)

三:自定义元类

1.先拿到1个类名:'People'

2.拿到 类的父类:(object,)

3.再拿到类的名称空间(运行类体代码得到的){...}

4.调用元类(传入类的3大要素:类名、基类、类的名称空间) 得到1个元类的对象,然后将元类的变量赋值给People(这个People就是我们用class自定义的类)

People = Mymeta('People', (object,), {...})

1.自定义元类

只有继承了type的类 才是自定义的元类

可以通过自定义的元类来规范类的产生

import re


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 not re.match('[A-Z]', class_name):
            raise BaseException('类名必须用驼峰体')

        # 至少继承一个类
        if len(class_bases) == 0:
            raise BaseException('至少继承一个类')

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


# People = Mymeta('People', (object,), {...})
class People(object, metaclass=Mymeta):
    '''
    这是我的文档注释
    '''
    school = 'Tinghua'

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

    def say(self):
        print(f'Hello {self.name}!')

2.探究是否可调用

①自定义的类实例化的对象和其他int、list都不可调用

class Foo:
    pass


obj1 = Foo()

obj2 = int(10)

obj3 = list([1, 2, 3])


# obj1()
# TypeError: 'Foo' object is not callable

# obj2()
# TypeError: 'int' object is not callable

# obj3()
# TypeError: 'list' object is not callable

②自定义的类加上__call__方法后,实例化的对象,是可以直接通过加()被调用的

调用对象 其实就是在调用对象类中 定义的绑定方法:__call__

class Foo:
    def __call__(self, *args, **kwargs):
        print('=========')
        print(self)
        print(args)
        print(kwargs)


obj1 = Foo()
obj1(1, 2, 3, a=1, b=2) # 调用对象 其实就是在调用对象类中 定义的绑定方法:__call__
# (1, 2, 3)
# {'a': 1, 'b': 2}

3.自定义元类 控制类的调用

import re


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 not re.match('[A-Z]', class_name):
            raise BaseException('类名必须用驼峰体')

        # 至少继承一个类
        if len(class_bases) == 0:
            raise BaseException('至少继承一个类')

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

    def __call__(self, *args, **kwargs):
        # 1.先创建一个空对象
        # 2.调用类的__init__方法,将空对象连同括号内的参数 一同传给__init__
        # 3.将初始化好的对象 赋值给变量名obj
        return 123


# People = Mymeta('People', (object,), {...})
class People(object, metaclass=Mymeta):
    '''
    这是我的文档注释
    '''
    school = 'Tinghua'

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

    def say(self):
        print(f'Hello {self.name}!')


people_obj = People()  # <class '__main__.People'>

print(people_obj)  # 123

4.自定义元类 控制类的调用 - 进阶

import re


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 not re.match('[A-Z]', class_name):
            raise BaseException('类名必须用驼峰体')

        # 至少继承一个类
        if len(class_bases) == 0:
            raise BaseException('至少继承一个类')

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

    # people_obj = People() 触发了下面的__call__
    def __call__(self, *args, **kwargs):
        # 1.先创建一个人的空对象
        people_obj = object.__new__(self)
        # 2.调用类的__init__方法,将空对象连同括号内的参数 一同传给__init__
        # People.__init__
        self.__init__(people_obj, *args, **kwargs)
        # 3.将初始化好的人对象 赋值给变量名people_obj
        return people_obj


# People = Mymeta('People', (object,), {...})
class People(object, metaclass=Mymeta):
    '''
    这是我的文档注释
    '''
    school = 'Tinghua'

    #      people_obj,'xxq',18
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def say(self):
        print(f'Hello {self.name}!')


people_obj = People('xxq', 18)  # <class '__main__.People'>

print(people_obj)  # <__main__.People object at 0x00000200F400DD90>

print(people_obj.name)  # xxq
print(people_obj.age)  # 18
print(people_obj.say)  # <bound method People.say of <__main__.People object at 0x000001E2AE41DD90>>

# 调用People类的时候,发生了什么
# 1.先创建一个人(people)的空对象
# 2.调用类的__init__方法,将空独享连同括号内的参数 一同传给__init__
# 3.将初始化好的人对象 赋值给变量名people_obj

5.自定义元类 将类的属性隐藏

import re


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 not re.match('[A-Z]', class_name):
            raise BaseException('类名必须用驼峰体')

        # 至少继承一个类
        if len(class_bases) == 0:
            raise BaseException('至少继承一个类')

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

    # people_obj = People() 触发了下面的__call__
    def __call__(self, *args, **kwargs):
        # 1.先创建一个人的空对象
        people_obj = object.__new__(self)
        # 2.调用类的__init__方法,将空对象连同括号内的参数 一同传给__init__
        # People.__init__
        self.__init__(people_obj, *args, **kwargs)
        print(people_obj.__dict__)
        people_obj.__dict__ = {'_%s__%s' % (self.__name__, k): v for k, v in people_obj.__dict__.items()}
        # 3.将初始化好的人对象 赋值给变量名people_obj
        return people_obj


# People = Mymeta('People', (object,), {...})
class People(object, metaclass=Mymeta):
    '''
    这是我的文档注释
    '''
    school = 'Tinghua'

    #      people_obj,'xxq',18
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def say(self):
        print(f'Hello {self.name}!')


people_obj = People('xxq', 18)
# <class '__main__.People'>
# {'name': 'xxq', 'age': 18}

print(people_obj.__dict__)
# {'_People__name': 'xxq', '_People__age': 18}
posted @ 2020-08-18 14:33  轻描丨淡写  阅读(167)  评论(0编辑  收藏  举报