💎一站式轻松地调用各大LLM模型接口,支持GPT4、智谱、星火、月之暗面及文生图 广告
[TOC] ## 知识储备 ### 1.exec函数 * exec:三个参数 参数一:字符串形式的命令 参数二:全局作用域(字典形式),如果不指定,默认为globals() 参数三:局部作用域(字典形式),如果不指定,默认为locals() * **exec的使用** 可以把exec命令的执行当成是一个函数的执行,会将执行期间产生的名字存放于局部名称空间中 ~~~ g={'x':1,'y':2} l={} exec(''' global x,z x=100 z=200 m=300''',g,l) print(g) #输出:{'x': 100, 'y': 2,......,'z':200} print(l) #输出:{'m': 300} ~~~ ### 2.一切皆对象 python中一切皆是对象,具体来说对象都有这四个特征,也可以说有这四个特征的都是对象 1. 都可以被引用,x=obj 2. 都可以当作函数的参数传入 3. 都可以当作函数的返回值 4. 都可以当作容器类的元素,如`l=[func,time,obj,1]` 既然一切皆是对象,那类本身也是一个对象,那类又是由哪个类产生的呢? ~~~ class Foo: pass obj=Foo() #type函数可以查看类型,也可以用来查看对象的类,二者是一样的 print(type(obj)) # 输出:<class '__main__.Foo'> 表示,obj 对象由Foo类创建 print(type(Foo)) # 输出:<type 'type'> 表示Foo对象有tupe类创建 ~~~ ### 3.type的语法 语法:`type(name, bases, dict)`,如:`type('Foo',(object,),{})` type 接收三个参数: * 第 1 个参数Foo是字符串,表示类名 * 第 2 个参数是元组 (object, ),表示所有的父类 * 第 3 个参数是字典,这里是一个空字典,表示没有定义属性和方法 ## 元类的知识 ### 什么是元类? 产生类的类称之为元类,默认所有用class定义的类,他们的元类是type 元类是用来控制如何创建类的,正如类是创建对象的模板一样,而元类的主要目的是为了控制类的创建行为 type是python的一个内建元类,用来直接控制生成类,python中任何class定义的类其实都是type类实例化的对象 ### 创建类方式1:使用class ~~~ class Chinese(object): country='China' def __init__(self,name,age): self.name=name self.age=age def talk(self): print('%s is talking' %self.name) ~~~ ### 创建类方式2:使用type 使用type模拟class创建类的过程,需要就是把class建类的步骤拆分开,手动去创建 * 准备工作: 定义类的三要素:类名,类的基类,类的名称空间(类体-->exec) 类的名称空间需要通过exec执行类体方法获取 * 定义类名,基类,类体 ~~~ class_name='Chinese' #类名 class_bases=(object,) #类的基类 #类体 class_body=""" country='China' def __init__(self,name,age): self.name=name self.age=age def talk(self): print('%s is talking' %self.name) """ ~~~ * 获取类的名称空间 类体定义的名字都会存放于类的名称空间中,我们可以事先定义一个空字典,然后用exec去执行类体的代码,生成类的局部名称空间,即填充字典 ~~~ class_dic={} exec(class_body,globals(),class_dic) print(class_dic) #输出: {'country': 'China',......} ~~~ * 调用元类type来产生类Chinense ~~~ Chinese1=type(class_name,class_bases,class_dic) obj1=Chinese1('noah',18) print(obj1,obj1.name,obj1.age) ~~~ ## 自定义元类控制类的行为 通过自定义的元类,可以做到控制类的创建,控制类的实例化,基于元类实现单例模式等,下面分别演示这三个功能 ### 1.控制类的创建行为 我们对类的创建做两个行为控制,第一个是类名首字母必须大写,第二个是必须写注释 >关于注释的说明:我们创建类的时候,如果有写备注的话,类的名称空间中就会有`__dic__`熟悉,可以通过判断这一熟悉是否存在并是否为空来判断是否有些注释 ~~~ #1.基于type自定义一个元类Mymeta class Mymeta(type): # 继承默认元类的一堆属性 def __init__(self, class_name, class_bases, class_dic): if '__doc__' not in class_dic or not class_dic.get('__doc__').strip(): raise TypeError('必须为类指定文档注释') if not class_name.istitle(): raise TypeError('类名首字母必须大写') super(Mymeta, self).__init__(class_name, class_bases, class_dic) #通过super直接继承type父类的初始化方法 #2. 基于Mymeta创建一个类people class People(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) ~~~ ### 2.控制类的实例化行为 1. 储备知识:`__call__`方法 在类的内置方法那里学过call方法,用途是将让对象可以再次被调用,而类之所以能被调用,就说明: 元类内部也应有有一个`__call__`方法,会在调用类的时触被执行 * 元类中的call方法需要完成三件事: 1:先造一个空对象obj 2:初始化对象obj 3:返回对象obj 2. 创建一个可控制实例化的元类 ``` # A.创建包含call方法的元类Mymeta class Mymeta(type): def __init__(self,class_name,class_bases,class_dic): super(Mymeta,self).__init__(class_name,class_bases,class_dic) def __call__(self, *args, **kwargs): #obj=Chinese('egon',age=18) #1:先造一个空对象obj obj=object.__new__(self) #2:初始化obj self.__init__(obj,*args,**kwargs) #3:返回obj return obj #B. 基于Mymeta创建一个类Chinese class Chinese(object,metaclass=Mymeta): country='China' def __init__(self,namem,age): self.name=namem self.age=age def talk(self): print('%s is talking' %self.name) #C.实例化对象obj obj=Chinese('noah',age=18) #等于:Chinese.__call__(Chinese,'egon',18) print(obj.__dict__) #输出:{'name': 'noah', 'age': 18} ``` ## 元类实现单例模式 ### 单例模式是什么 单例模式就是指,如果实例化的对象数据是一样的,就没有必要分配两个内存地址去存储它,以便节约内存,例如: ``` >>> a,b=1,1 >>> id(a),id(b) (1490068576, 1490068576) ``` 那如果是自定义的类,类中的数据不变化(如mqsql端口信息),实例化的对象ID一样吗? ~~~ class mysql: def __init__(self): self.host='127.0.0.1' self.port=3306 obj1=mysql() obj2=mysql() print(obj1) print(obj2) #输出: <__main__.mysql object at 0x000002519D69A240> <__main__.mysql object at 0x000002519D69A6D8> ~~~ ### 通过绑定类方法实现[常规] ``` class MySQL: __instance=None 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 obj1=MySQL.singleton() obj2=MySQL.singleton() print(obj1) print(obj2) #输出: <__main__.Mysql object at 0x0000019F876AA710> <__main__.Mysql object at 0x0000019F876AA710> ``` ### 通过自定义元类的方法实现 ``` class Mymeta(type): def __init__(self,class_name,class_bases,class_dic): super(Mymeta,self).__init__(class_name,class_bases,class_dic) self.__instance=None def __call__(self, *args, **kwargs): if not self.__instance: obj=object.__new__(self) self.__init__(obj) self.__instance=obj return self.__instance class Mysql(object,metaclass=Mymeta): def __init__(self): self.host='127.0.0.1' self.port=3306 def conn(self): pass obj1=Mysql() obj2=Mysql() print(obj1 is obj2) 输出: <__main__.Mysql object at 0x0000019220B4A908> <__main__.Mysql object at 0x0000019220B4A908> ```