【知识点】
1、isinstance() 判断对象所属类型,包含继承关系
type()与isinstance()的区别
1 class mystr(str):pass 2 ms=mystr('alex') 3 print(ms) 4 print(type(ms) is str) # 不包含继承关系,只管一层 5 print(isinstance(ms,str)) # 包含所有的继承关系
2、issubclass(B,A) 判断B是不是A的子类(判断类与类之间的继承关系)
3、反射:用字符串数据类型的变量名来访问这个变量的值
方法:getattr hasattr setattr delattr
(1)在类的应用:静态属性(静态字段)、类方法、静态方法
命名空间.XXX=getattr(命名空间,'XXX') 属性直接用,方法在后加()
1 class Student: 2 ROLE='STUDENT' 3 4 @classmethod 5 def check_course(cls): 6 print("查看课程了") 7 8 @staticmethod 9 def login(): 10 print("登录") 11 # 反射查看属性 12 print(Student.ROLE) # 通过类名调用 13 print(getattr(Student,'ROLE')) 14 15 # 反射调用方法 16 getattr(Student,'check_course')() # 查看课程了 # 类方法 17 getattr(Student,'login')() # 登录 # 静态方法 18 19 num=input("请输入要调用的方法>>>") 20 if hasattr(Student,num): # 如果输入的num是Student这个命名空间里的方法,则返回True,否则返回Flase 21 getattr(Student,num)() 22 else: 23 num = input("你输入的有误,请重新输入要调用的方法>>>")
(2)在对象中应用:
1 # 在对象中 2 class A(): 3 def __init__(self,name): 4 self.name=name 5 6 def func(self): 7 print("in func") 8 9 a=A('123') 10 print(a.name) # 123 11 print(getattr(a,'name')) # 123 12 getattr(a,'func')() # in func
(3)在模块中
1 # 在模块 2 import os 3 ''' 4 # os.rename('__init__.py','init') 5 # getattr(os,'rename')('init','__init__.py') 6 rename=os.rename 7 rename2=getattr(os,'rename') 8 rename('__init__.py','init') # os.rename('__init__.py','init') 9 rename2('init','__init__.py') # getattr(os,'rename')('init','__init__.py') 10 ''' 11 # 反射自己模块中的内容,找到自己当前文件所在的命名空间 12 def wahaha(): 13 print("wahaha") 14 15 def qqxing(): 16 print("qqxing") 17 18 # wahaha() 19 # qqxing() 20 21 import sys 22 # print(sys.modules) 23 # sys.modules 这个方法,表示所有在当前这个python程序中导入的模块 24 # print(sys.modules['__main__']) 25 my_file=sys.modules['__main__'] # 当前文件所在模块 26 # my_file.wahaha() 27 28 getattr(my_file,'wahaha')() # wahaha
【反射总结】
hasattr getattr
① 类名.名字
getattr(类名,'名字')
② 对象名.名字
getattr(对象,'名字')
③ 模块名.名字
import 模块
getattr(模块,'名字')
④ 自己文件.名字
import sys
getattr(sys.modules['__main__'],'名字')
【反射的应用】
1 # 一个选课查询系统 2 class Manager(): 3 OPERATE_DIC=[ 4 ('create_student','创建学生账号'), 5 ('create_course','创建学生课程'), 6 ('check_student','查看学生信息') 7 ] 8 9 def __init__(self,name): 10 self.name=name 11 12 def create_student(self): 13 print("创建学生账号") 14 15 def create_course(self): 16 print("创建学生课程") 17 18 def check_student(self): 19 print("查看学生信息") 20 21 22 class Student(): 23 OPERATE_DIC = [ 24 ('choose_course', '学生选择课程'), 25 ('check_course', '学生查询课程') 26 ] 27 28 def __init__(self,name): 29 self.name=name 30 31 def choose_course(self): 32 print("学生选择课程") 33 34 def check_course(self): 35 print("学生查询课程") 36 37 38 def login(): 39 count=1 40 while count<4: 41 username=input("请输入用户名:") 42 password=input("请输入密码:") 43 with open('userinfo',mode='r',encoding='utf-8') as f: 44 for line in f: 45 user,pwd,ident=line.strip().split('|') 46 if username == user and password == pwd: 47 print("登录成功") 48 return username,ident 49 else: 50 print("输入错误,请重新输入,你还有%d次机会" % (3-count)) 51 break 52 count+=1 53 54 import sys 55 def main(): 56 while True: 57 user,id=login() 58 file=sys.modules['__main__'] # 拿到自己当前所在的模块 59 cls=getattr(file,id) # id=manager,student 所以可以拿到相应的类 60 obj=cls(user) # 类的实例化,并传入参数 61 lst=getattr(cls,'OPERATE_DIC') # 通过类拿到静态属性值 62 for num,el in enumerate(lst,1): # enumerate()是将列表中元素添加上序号,从1开始则需要添加起始参数1 63 print(num,el[1]) # num是序号,el是元组 el[1]获取元组的第二个参数 64 i=int(input('请输入要选择项序号:')) 65 getattr(obj,lst[i-1][0]) () # lst[i-1][0]得到的是字符串,故用getattr()方法 66 main()
4、内置函数
__名字__
# 类中的特殊方法内置方法
#双下方法
#魔术方法
类中的每一个双下方法都有它自己的特殊意义
(1)__call__ 方法
对象() 相当于调用__call__方法
类名()() 相当于先实例化一个对象,再对对象()。与上面结果一样
1 # __call__ 2 class A: 3 def __call__(self,*args,**kwargs): 4 print("执行call方法了") 5 6 class B: 7 def __init__(self,cls): 8 print("在实例化A之前做一些事") 9 self.a=cls() 10 self.a() 11 print("在实例化A之后做一些事") 12 # a=A() 13 # a() # 相当于调用__call__方法 14 #A()() # 和上面结果一样,相当于调用__call__ 15 B(A)
(2)__len__方法
len(obj) 相当于调用了这个obj的__len__方法
__len__方法return的值就是len函数返回的值
#如果一个obj对象没有__len__方法,那么len函数就会报错
1 # __len__方法 2 #内置函数与类的内置方法是有奸情的 3 class Mylist: 4 def __init__(self): 5 self.lst=[1,2,3,4,5,6] 6 self.name='123' 7 self.age=45 8 9 def __len__(self): 10 print("执行__len__方法了") 11 return len(self.__dict__) 12 m=Mylist() 13 print(len(m)) # 执行__len__方法了 3
(3)__new__方法(构造方法)
在执行__init__之前,开辟一个空间。
1 # __new__构造方法 2 class Single: 3 def __new__(cls,*args,**kwargs): 4 obj=object.__new__(cls) 5 print("在new方法里",obj) 6 return obj 7 8 def __init__(self): 9 print("在init方法里",self) 10 #1、开辟一个空间,属于对象的 11 #2、把这个对象的空间传给self,执行init 12 #3、将这个对象的空间返回给调用者 13 obj=Single() #结果:在new方法里 <__main__.Single object at 0x0000019F309D1978> 14 #结果:在init方法里 <__main__.Single object at 0x0000019F309D1978> 15 #若是类中没有__nem__方法,则会调用object里的__new__来开辟空间
【一道面试题】
写出一个单例类?
#1、单例:如果一个类,从头到尾只能有一个实例(说明从头到尾只开辟了一块属于对象的空间),那么这个类就是一个单例类。
单例的例子代码:
1 # 写一个单例类 2 class Single: 3 __ISINSTANCE=None 4 def __new__(cls,*args,**kwargs): 5 if not cls.__ISINSTANCE: 6 cls.__ISINSTANCE=object.__new__(cls) 7 return cls.__ISINSTANCE 8 9 def __init__(self,name,age): 10 self.name=name 11 self.age=age 12 13 s1=Single('太白',23) 14 s2=Single('Alex',26) 15 print(s1.name) # Alex 16 print(s2.name) # Alex 17 # 原因:首先开辟一个空间,并且只有一个空间。最开始将name赋值“太白”,age赋值23, 18 # 然后执行S2=Single('Alex',26),由于只有一个空间,故将此前的值覆盖掉,所以最后打印都是“Alex”
注意:上述例子首先开辟了一个空间,并且只有一个空间。最开始将name赋值“太白”,age赋值23,然后执行S2=Single('Alex',26),由于只有一个空间,故将此前的值覆盖掉,所以最后打印都是“Alex”。
(4)__str__方法
print(对象) 相当于调用一个对象的__str__方法
str(obj) 相当于执行了obj.__str__方法
'%s' % obj 相当于执行obj.__str__方法
1 # __str__方法 2 class Student: 3 def __str__(self): 4 return '%s %s %s' % (self.school,self.stu_cls,self.name) 5 6 def __init__(self,name,stu_cls): 7 self.school='oldboy' 8 self.name=name 9 self.stu_cls=stu_cls 10 11 he=Student('菏泽微','py14') 12 # print(he) # 执行了__str__方法 结果:oldboy py14 菏泽微 13 # print(str(he)) # 也执行了__str__方法,结果同上 14 print('学生1:%s' % he) # 也执行了__str__方法,结果同上
【小结】所有的双下方法,没有需要你在外面调用的,而是总有一些其他的 内置函数 特殊的语法 来自动触发这些双下方法