元类介绍 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()