一、补充内置函数isinstance和issubclass
1、isinstance是判断一个对象是不是由一个对象产生的
1 class Foo: 2 pass 3 4 obj=Foo() 5 6 print(isinstance(obj,Foo)) #判断一个对象是否是由某个类调用产生 7 8 # 在python3中统一类与类型的概念 9 d={\'x\':1} #d=dict({\'x\':1} #) #类即类型,d是一个对象,dict是一个类,d是由调用dict产生的对象 10 11 print(type(d) is dict) #type也可以判断数据类型,但这并不是type的的主要功能,其实type是一个元类 12 print(isinstance(d,dict))
2、issubclass是判断一个l类是不是另外一个类的子类
# issubclass() class Parent: pass class Sub(Parent): pass print(issubclass(Sub,Parent)) #判断类Sub是不是Parent是不是的子类,是布尔值为True print(issubclass(Parent,object))
二、反射
\'\'\' 1、什么是反射 通过字符串来操作类或者对象的属性 2、如何用 hasattr getattr setattr delattr \'\'\' class People: country=\'China\' def __init__(self,name): self.name=name def eat(self): print(\'%s is eating\' %self.name) peo1=People(\'egon\') # hasattr:判断-------(对象)---------中有没有一个------(字符串形式的属性)-------对应的数据属性或函数属性,结果是一个bool值 print(hasattr(peo1,\'eat\')) #效果等价:peo1.eat # # getattr:判断-------(对象)---------中有没有一个------(字符串形式的属性)-------对应的数据属性或函数属性,如果有对应的数据属性,返回的是数据属性对应的值,如果有对应的函数属性返回的是一个内存地址 print(getattr(peo1,\'eat\')) #peo1.eat #类中有这个属性,返回的是一个内存地址 print(getattr(People,\'country\')) #类中有数据属性,返回的是数据属性对应的属性值 print(getattr(peo1,\'xxxxx\',None)) #None是一个默认值,如果类中没有这个数据属性、函数属性就会将默认值返回,如果没有默认值,类中也没有字符串对应的属性,就会报错 # setattr:判断-------(对象)---------中有没有一个------(字符串)-------对应的数据属性,有可以对其属性值进行修改,没有添加新的属性 setattr(peo1,\'age\',18) #peo1.age=18 #可以给对象添加新的属性 setattr(peo1,\'name\',\'alex\') #可以修改对象的属性值 print(peo1.age) print(peo1.name) # print(peo1.__dict__) # delattr:判断-------(对象)---------中有没有一个------(字符串形式的属性)-------对应的数据属性或函数属性,有可以将其删除 delattr(peo1,\'name\') #del peo1.name print(peo1.__dict__) delattr(peo1,\'eat\') #-----会报错,setattr和delattr只会对自己名称空间的名字做修改和删除,并不能改变类名称空间中的属性名 \'\'\'重点\'\'\' # 反射的应用: class Ftp: def __init__(self,ip,port): self.ip=ip self.port=port def get(self): print(\'GET function\') def put(self): print(\'PUT function\') def run(self): while True: choice=input(\'>>>: \').strip() #模拟用户实际输入对应的命令是一个字符串,所以此时就要用到映射 \'\'\'方法一\'\'\' # # print(choice,type(choice)) # if hasattr(self,choice): #判断用户输入的字符串形式的属性名是否存在,拿到一个返回的bool值 # method=getattr(self,choice) #输入的字符串形式的属性名存在,通过属性名拿到绑定方法的内存地址 # method() #绑定方法加括号即调用 # else: # print(\'输入的命令不存在\') \'\'\'方法二\'\'\' method=getattr(self,choice,None) #如果用户输入的字符串形式的属性值存在,则直接拿到该属性的绑定方法的内存地址返回,属性是不存在,返回None if method is None: print(\'输入的命令不存在\') else: method() #函数属性的内存地址加括号直接调用 conn=Ftp(\'1.1.1.1\',23) #类加括号,产生一个空对象,并对对象通过__init__进行初始化 conn.run() #将对象绑定给方法,会将对象当做第一个参数自动传入
三、自定义方法来定义类的功能
1、__str__方法-------会在打印对象的时候自动触发
class People: def __init__(self,name,age): self.name=name self.age=age #在对象被打印时,自动触发,应该在该方法内采集与对象self有关的信息,然后拼成字符串返回 def __str__(self): # print(\'======>\') return \'<name:%s age:%s>\' %(self.name,self.age) #返回的必须是字符串,否则就会报错 obj=People(\'egon\',18) obj1=People(\'alex\',18) print(obj) #obj.__str__() #没有__str__打印对象的结果就是一个内存地址, # 但是有了__str__我们就可以拼接任意我们想要的样式,此时在打印对象就会得到我们拼接的结果 print(obj) #obj.__str__() print(obj) #obj.__str__() print(obj1) #obj1.__str__() """ 默认就有str,只是返回的是内存地址,我们可以通过设置str,可以通过打印对象打印出好看的格式,而不是只是单纯的打印出一个内存地址""" d={\'x\':1} #d=dict({\'x\':1}) print(d) #打印d这个对象是一个字典,而不是像上面一样是一个内存地址,是因为dict内一定自带了一个__str__,在打印对象的时候自动触发
2、__del__会在对象被删除时自动触发
# """1、__del__析构方法""" # # __del__会在对象被删除时自动触发 class People: def __init__(self,name,age): self.name=name self.age=age self.f=open(\'a.txt\',\'rt\',encoding=\'utf-8\') #打开该文件占用两方面的资源,一是应用程序即Python解释器,Python会自动回收,另一方面是系统资源,要手动关闭 def __del__(self): #删除对象是自动触发 print(\'run=-====>\') # 做回收系统资源相关的事情 self.f.close() #删除应用程序资源要在关闭系统资源之后,要不就会出现系统资源无法关闭,白白占用资源 obj=People(\'egon\',18) print(\'主\') #系统程序运行完毕,回收资源,会自动触发__del__的执行,所以先打印:\'主\',再打印:\'run=-====>\'
四、元类
\'\'\' 1、什么是元类 在python中一切皆对象,那么我们用class关键字定义的类本身也是一个对象 负责产生该对象的类称之为元类,即元类可以简称为类的类 class Foo: # Foo=元类() #一切皆对象,类加括号产生对象 pass 2、为何要用元类 元类是负责产生类的,所以我们学习元类或者自定义元类的目的 是为了控制类的产生过程,还可以控制对象的产生过程 3、如何用元类 \'\'\' #1、储备知识:内置函数exec的用法 cmd=""" x=1 def func(self): pass """ class_dic={} exec(cmd,{},class_dic) #exec会将cmd字符串中的代码拿出来执行一次,将产生的名字丢掉事先定义好的class_dic空字典中 print(class_dic) #{\'x\': 1, \'func\': <function func at 0x00000267165F92F0>} #2、创建类的方法有两种 # 大前提:如果说类也是对象的话,那么用class关键字去创建类的过程也是一个实例化的过程 # 该实例化的目的是为了得到一个类,调用的是元类 #2.1 方式一:用的默认的元类type class People: #People=type(...)--------默认的元类type实例化出一个对象Pelple,实例化的结果也是一个对象 country=\'China\' def __init__(self,name,age): self.name=name self.age=age def eat(self): print(\'%s is eating\' %self.name) peo=People(\'EGON\',18) print(peo) #------------<__main__.People object at 0x000001F635282E10>*********调用类实例化出对象 print(type(People)) #------------<class \'type\'>*****************************************调用元类实例化出类 """重点""" #2.1.1 创建类的3个要素:类名,基类,类的名称空间 class_name=\'People\' #类名,是一个字符串,---------由上面的class定义类我们知道,创建类的三要素:类名,基类,类的名称空间 class_bases=(object,) #基类,----------------------我们通过__bases__,知道基类是一个元组的形式 class_dic={} #类的名称空间,---------------通过__dict__,知道类的名称空间的是一个字典 class_body=""" country=\'China\' def __init__(self,name,age): self.name=name self.age=age def eat(self): print(\'%s is eating\' %self.name) """ #--------将类体代码放到一个字符串中 exec(class_body,{},class_dic)#执行字符传中的代码,将产生的名字方到class_dic的名称空间中,即之前定义类将产生的名字放到类的名称空间中 # 准备好创建类的三要素 # print(class_name) #-------People # print(class_bases) #-------(<class \'object\'>,) # print(class_dic) #-------{\'country\': \'China\', \'__init__\': <function __init__ at 0x00000222D55F92F0>, \'eat\': <function eat at 0x00000222DC618BF8>} # People=type(类名,基类,类的名称空间) #调用元类就可以产生一个类这个对象 People1=type(class_name,class_bases,class_dic) #将事先定义好的类的三要素放到当做参数传给元类,调用元类即产生对象 print(People1) #--------<class \'__main__.People\'>自定义类产生的结果 obj1=People1(\'egon\',18) print(People) #--------<class \'__main__.People\'>,class定义类产生的结果 obj=People(\'egon\',18) obj1.eat() obj.eat() """----------------------------------------重点----------------------------------------""" #2.2 方式二:用的自定义的元类 class Mymeta(type): #只有继承了type类才能称之为一个元类,否则就是一个普通的自定义类 def __init__(self,class_name,class_bases,class_dic): print(self) #现在是People print(class_name) print(class_bases) print(class_dic) super(Mymeta,self).__init__(class_name,class_bases,class_dic) #重用父类的功能 # 分析用class自定义类的运行原理(而非元类的的运行原理): #1、拿到一个字符串格式的类名class_name=\'People\' #2、拿到一个类的基类们class_bases=(obejct,) #3、执行类体代码,拿到一个类的名称空间class_dic={...}------------------前三步就是造类的三要素 #4、调用People=type(class_name,class_bases,class_dic)----------------调用元类(类)产生类(对象)------------调用类产生对象 class People(object,metaclass=Mymeta): #People=Mymeta(类名,基类们,类的名称空间)------metaclass=Mymeta是自定义的元类名 country=\'China\' def __init__(self,name,age): self.name=name self.age=age def eat(self): print(\'%s is eating\' %self.name) """----------------------------------------重点----------------------------------------""" """应用:自定义元类控制类的产生过程,类的产生过程其实就是元类的调用过程------(对象的产生过程就是调用类的过程)""" class Mymeta(type): #只有继承了type类才能称之为一个元类,否则就是一个普通的自定义类---------------必须要继承type类 def __init__(self,class_name,class_bases,class_dic): #在自定义类之上添加逻辑判断 if class_dic.get(\'__doc__\') is None or len(class_dic.get(\'__doc__\').strip()) == 0: #必须有文档注释,且不为空 raise TypeError(\'类中必须有文档注释,并且文档注释不能为空\') if not class_name.istitle(): #类的首字母必须大写 raise TypeError(\'类名首字母必须大写\') super(Mymeta,self).__init__(class_name,class_bases,class_dic) #重用父类的功能 class People(object,metaclass=Mymeta): #People=Mymeta(\'People\',(object,),{....}) """这是People类""" country=\'China\' def __init__(self,name,age): self.name=name self.age=age def eat(self): print(\'%s is eating\' %self.name) #3 储备知识:__call__ class Foo: def __call__(self, *args, **kwargs): print(self) #<__main__.Foo object at 0x000002193E892E10> print(args) #(1, 2, 3)----------------*args接收位置参数,存成元组的形式 print(kwargs) #{\'x\': 1, \'y\': 2}---------**kwargs接收关键字参数,存成字典的形式 obj=Foo() #调用类不会自动触发,会在调用对象时自动触发,通过self也可以看出,是调用对象时自动触发 # # 要想让obj这个对象变成一个可调用的对象,需要在该对象的类中定义一个方法__call__方法 # # 该方法会在调用对象时自动触发 # obj(1,2,3,x=1,y=2) #调用对象时自动触发__call__方法,并将对象自动传入 """-----------------------------------------------重点---------------------------------------------""" # 4、自定义元类来控制类的调用的过程,即类的实例化过程 class Mymeta(type): def __call__(self, *args, **kwargs): #会在调用对象时自动触发,此时的对象时一个类,即People # print(self) # self是People # print(args) # print(kwargs) # return 123 """调用类产生一个对象,发生两件事""" #和class定义类,调用类一样发生两件事 # 1、先造出一个People的空对象 obj=self.__new__(self) #造出了一个自定义类People的空对象 # 2、为该对空对象初始化独有的属性 # print(args,kwargs) self.__init__(obj,*args,**kwargs) #对空对象进行初始化,空对象传入,以及参数原封不动的传入 # 3、返回一个初始好的对象 return obj #将造出的对象返回, \'\'\'**********************************看成一个对象************************************************\'\'\' class People(object,metaclass=Mymeta): #自定义类People,元类是Mymeta,元类必须继承type类,否则就不是元类 country=\'China\' def __init__(self,name,age): self.name=name self.age=age def eat(self): print(\'%s is eating\' %self.name) def __new__(cls, *args, **kwargs): #对象自己中有__new__属性,先从对象自己的名称空间中找,自己没有在到自己的类中找 print(cls) # cls.__new__(cls) # 错误 #自己有调用了自己的__new__,这样就出现无线递归,所以会报错 obj=super(People,cls).__new__(cls) #自己中有,我们任然让其取继承父类中的__new__属性,来产生一个空对象,然后将对象初始化,拿到一个返回值 return obj \'\'\'**********************************看成一个对象************************************************\'\'\' """-----------------------------------------------重点---------------------------------------------""" # 分析:调用Pepole的目的 #1、先造出一个People的空对象 #2、为该对空对象初始化独有的属性 # obj1=People(\'egon1\',age=18) # obj2=People(\'egon2\',age=18) # print(obj1) # print(obj2) # obj=People(\'egon\',age=18) # print(obj.__dict__) # print(obj.name) # obj.eat()