元类介绍  meta

储备知识exec,可以被当作函数来看待。

参数1:字符串形式的命令

参数2:全局作用域(字典形式),如果不指定默认就使用globals()

参数3:局部作用域(字典形式),如果不指定默认就使用locals()

g = {'x':1, 'y':2}
l = {}
exec('''
global x,m
x = 10
m = 100

z = 3
''', g, l)

python中一切皆对象,对象可以怎么用?

1.都可以被引用,x = obj

2.都可以当作函数的参数传入

3.都可以当作函数的返回值

4.都可以当作容器类的元素,l = {func, time, obj, 1}

# 类也是对象
class Foo:
    pass

obj = Foo()

产生类的类称之为元类,默认所有用class定义的类,他们的类是type

定义类的两种方式:

# 方法一:class
class Chinese:  # Chinese = type(...)
    country = 'China'
    
    def __init__(self, name, age):
        self.name = name
        self.age = age
    
    def talk(self):
        print('%s is talking' % self.name)

# 方法二:type

定义类的三要素:类名,类的基类们,类的名称空间

class_name = 'Chinese'
class_bases = (object,)
class_body = '''
class Chinese:  # Chinese = type(...)
    country = 'China'
    
    def __init__(self, name, age):
        self.name = name
        self.age = age
    
    def talk(self):
        print('%s is talking' % self.name)
'''
class_dic = {}
exec(class_body, globals(), class_dic)
print(class_dic)  # {'Chinese': <class '__main__.Chinese'>}

Chinese = type(class_name, class_bases, class_dic)
print(Chinese)  # <class '__main__.Chinese'>

自定义元类控制类的创建

class Mymeta(type):

    def __init__(self, class_name, class_bases, class_dic):
        if not class_name.istitle():
            raise TypeError('类名首字母必须大写')
        
        if '__doc__' not in class_dic or not class_dic['__doc__'].strip():
            raise TypeError('必须有注释且注释不能为空')
        
        super(Mymeta, self).__init__(class_name, class_bases, class_dic)

class Chinese(object, metaclass=Mymeta):
    country = 'China'
    
    def __init__(self, name, age):
        self.name = name
        self.age = age
    
    def talk(self):
        print('%s is talking' % self.name)

# Chinese = Mymeta(class_name, class_bases, class_dic)

自定义元类控制类的实例化行为

知识储备

# 知识储备__call__方法
class Foo:
    def __call__(self, *args, **kwargs):
        print(self)
        print(args)
        print(kwargs)

obj = Foo()
obj(1, 2, 3, a=1, b=2, c=3)  # obj.__call__(obj, 1, 2, 3, a=1, b=2, c=3)

# 元类内部也应该有一个__call__方法,会在调用Foo时触发执行
# Foo(1, 2, x=1)  # Foo.__call__(Foo, 1, 2, x=1)

实例化行为

class Mymeta(type):

    def __init__(self, class_name, class_bases, class_dic):
        if not class_name.istitle():
            raise TypeError('类名首字母必须大写')

        if '__doc__' not in class_dic or not class_dic['__doc__'].strip():
            raise TypeError('必须有注释且注释不能为空')

        super(Mymeta, self).__init__(class_name, class_bases, class_dic)

    def __call__(self, *args, **kwargs):  # obj = Chinese('egon', age=18)
        # print(self)  # self = Chinese
        # print(args)  # args = ('egon')
        # print(kwargs)  # kwargs = ('age':18)

        # 第一件事:先造一个空对象obj
        obj = object.__new__(self)
        # 第二件事:初始化obj
        self.__init__(obj, *args, **kwargs)
        # 第三件事:返回obj
        return obj

class Chinese(object, metaclass=Mymeta):
    '''abc'''
    country = 'China'
    
    def __init__(self, name, age):
        self.name = name
        self.age = age
    
    def talk(self):
        print('%s is talking' % self.name)

# Chinese = Mymeta(class_name, class_bases, class_dic)

obj = Chinese('egon', age=18)  # 调用即触发__call__, Chinese.__call__(Chinese,'egon',18)

自定义元类控制类的实例化行为的应用

优化策略 :当两个变量赋值一致时,就使他们俩指向同一个id,节省内存空间

>>> obj1 = int(1) >>> obj2 = int(1) >>> id(obj1) 140726029840128 >>> id(obj2) 140726029840128

单例模式

# 单例模式:当对象内部特征一样的时候,就不会再次产生新的空间,只使用原来的空间
class MySQL:
    __instance = None  # __instance = obj1
    
    def __init__(self):
        self.host = '127.0.0.1'
        self.port = 3306

    @classmethod
    def singleton(cls):
        if not cls.__instance:
            obj = cls()
            cls.__instance = obj
        return cls.__instance

    def conn(self):
        pass

    def execute(self):
        pass

obj1 = MySQL.singleton()
obj2 = MySQL.singleton()

实现方式二:元类的方式

class Mymeta(type):

    def __init__(self, class_name, class_bases, class_dic):
        if not class_name.istitle():
            raise TypeError('类名首字母必须大写')

        if '__doc__' not in class_dic or not class_dic['__doc__'].strip():
            raise TypeError('必须有注释且注释不能为空')

        super(Mymeta, self).__init__(class_name, class_bases, class_dic)
        self.__instance = None

    def __call__(self, *args, **kwargs):  # obj = Chinese('egon', age=18)
        if not self.__instance:
            obj = object.__new__(self)  # 造空对象
            self.__init__(obj)  # 初始化
            self.__instance = obj  # 赋值

        return self.__instance

class Mysql(object, metaclass=Mymeta):
    '''Mysql xxx'''
    __instance = None  # __instance = obj1

    def __init__(self):
        self.host = '127.0.0.1'
        self.port = 3306

    def conn(self):
        pass

    def execute(self):
        pass


obj1 = Mysql()
obj2 = Mysql()