1.内置函数补充
1)isinstance(对象,类)
作用:用于判断对象属不属于这个类型,继承的类也算(即一个子类的对象,通过isinstance判断它是否属于父类时,返回的也是True)
#判断对象所属类型,包括继承关系
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
class A: pass class B(A): pass b = B() print(isinstance(b,B)) print(isinstance(b,A)) 输出: True True
备注:若【type(子类的对象) is 类】方式判断一个对象是否属于这个类时,只有当前类才返回True,若是父类则返回False
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
class A: pass class B(A): pass b = B() print(isinstance(b,B)) print(isinstance(b,A)) print(type(b) is A) #父类则返回False 输出: True True False True
总结:type()不包含继承关系,只管一层;isinstance,包含所有的继承关系
补充:【==】用来判断内存地址是否相等,【i】s用来判断内存地址是否相同,所以is要求更 苛刻,不仅要求值相等,要要求内存地址相等
2)issunclass(A类,B类)
作用:判断类于类之间的继承关系;即A类是不是B类的子类
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
class A(): pass class B(A): pass print(issubclass(A,B)) print(issubclass(B,A)) #B是A的子类,返回True 输出: False True
2.反射
1)什么是反射:用字符串数据类型的变量名来访问这个变量的值
2)反射的方法:getattr() hasattr() setattr() delattr()
2)反射的使用
①使用类去反射
格式:
类命名空间.XXX == getattr(命名空间,'XXX')
#引:不使用反射的情况下,编写一个学生选课系统
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
class Student: def __init__(self) :pass def check_course(self): print("查看课程") def choose_course(self): print("选择课程") def choosed_course(self): print("查看已选课程") s = Student() content = input(">>>") if content == "check_course": s.check_course() elif content == "choose_course": s.choose_course() elif content == "choosed_course": s.choosed_course() 输入输出: >>>check_course 查看课程
问题:对于如上程序,如果学生类中有很多的方法,当根据用户输入,判断哪一个方法执行时,若是有多个方法都需要去一一判断,代码变的非常的冗长;并且如果用户
#通过反射,拿到类的静态变量(即静态字段,也叫静态属性)的值
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
class Student: ROLE = "STUDENT" #ROLE就是变量 print(Student.ROLE) a = getattr(Student,'ROLE') #'ROLE'是字符串类型,即用字符串数据类型的变量名访问到了变量的值 print(a) 输出: STUDENT STUDENT
注:对于用户输入的内容和网络传输的内容不能直接用eval()去执行,容易出现安全问题;只有eval()明确的写在自己的代码里,才能去使用
说明:getattr(Student,'ROLE')方法中,第一个参数的命名空间中的变量名为第二个参数的变量的值,即从Student这个类的命名空间中取到ROLE这个变量的值
#通过反射,拿到类的类方法的值
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
class Student: @classmethod def check_course(cls): print("查看所有课程") print(getattr(Student,'check_course')) #打印方法的地址值 getattr(Student,'check_course')() #通过反射调用类方法 输出: <bound method Student.check_course of <class '__main__.Student'>> 查看所有课程
#通过反射,拿到类的静态方法的值
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
class Student: @staticmethod def login(): print("用户登录") print(getattr(Student,'login')) #打印方法的地址值 getattr(Student,'login')() #通过反射调用静态方法 输出: <function Student.login at 0x000002317FF39048> 用户登录
#使用反射的方式根据用户的输入,调用相应的方法(又可能用户输入的是一个没有的东西)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
class Student: ROLE = "STUDENT" @classmethod def check_course(cls): print("查看所有课程") @staticmethod def login(): print("用户登录") content = input(">>>") getattr(Student,content)() #用户输入什么就执行什么,如果输入的内容不存在,程序直接报错 输出: >>>check_course 查看所有课程 >>>aaa AttributeError: type object 'Student' has no attribute 'aaa
#将hasattr()和getattr()一起使用,hasattr()表示输入的字符串在类的命名空间中是否可以找到,能找到返回True,不能找到返回False
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
class Student: ROLE = "STUDENT" @classmethod def check_course(cls): print("查看所有课程") @staticmethod def login(): print("用户登录") content = input(">>>") print(hasattr(Student,content)) getattr(Student,content)() #用户输入什么就执行什么,如果输入的内容不存在,程序直接报错 输出: >>>login True 用户登录
②使用对象去反射
#通过反射,拿到对象的对象属性和方法
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
class A: def __init__(self,name): self.name = name def func(self): print("In func...") a = A("阿狸") print(getattr(a,'name')) #通过对象去反射对象属性 getattr(a,'func')() #通过对象去反射方法 输出: 阿狸 In func...
③使用模板去反射
#以os模块中的rename方法举例
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
import os #os.rename('a1.txt','a2.txt') #将a1.txt改名为a2.txt getattr(os,'rename')('a2.txt','a1.txt') #利用反射,将a2.txt改名为a1.txt print(os.rename) print(getattr(os,'rename')) 输出: <built-in function rename> <built-in function rename>
总结:os.rename 就相当于getattr(os,'rename')
④反射自己模块中的内容
#正常情况下调用本程序中的函数
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
def lol(): print("英雄联盟") def dnf(): print("地下城勇士") lol() dnf() 输出: 英雄联盟 地下城勇士
#反射自己模块中的函数
#先找到自己当前文件所在的命名空间
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
import sys #导入sys模块。该模块中的所有东西都是和python解释器相关的 print(sys.modules) #sys.modules表示所有在当前这个python程序导入的模块 print(sys.modules['__main__']) #查找当前所在模块的内存地址 输出: '__main__': <module '__main__' from 'E:/python/project/untitled/练习/基础代码练习.py'>, 表示当前文件的内存地址
#再通过获取到的当前文件的内存地址,去反射本模块中的函数
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
def lol(): print("英雄联盟") def dnf(): print("地下城勇士") import sys file = sys.modules['__main__'] getattr(file,'lol')() getattr(file,'dnf')() 输出: 英雄联盟 地下城勇士
3)setattr()和delattr()
#setattr()给修改属性的值()很少用该方式给一个属性重新赋值
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
class A: def __init__(self,name): self.name = name a = A("阿狸") setattr(a,'name',"九尾妖狐") print(a.name) 输出: 九尾妖狐
#delattr()删除属性的值
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
class A: def __init__(self,name): self.name = name a = A("阿狸") print(a.__dict__) delattr(a,'name') print(a.__dict__) 输出: {'name': '阿狸'} {}
反射总结:
①方法:hasattr(),getattr(),setattr()
②使用类名的反射:类名.名字 ;getattr(类名,'名字')
③使用对象名的反射:对象名.名字;getattr(对象,'名字')
③模块的反射:模块名.名字;getattr(模块,'名字')
④自己模块的反射:模块名.名字;getattr(sys.modules['__main__'],'名字')
例:学生选课系统
#若不要反射,对于一个学生选课系统,当通过login方法登录时,先判断身份,并且根据身份实例化对象,再根据每个身份对应的类,让用户选择能够做的事,这种方式,代码会很长
通过反射实现
#先在userinfo文件中写入用户的用户名、密码、身份如下 -------------------------------------------------------- jianji|123456|Manager ali|qwer|Student guangtou|123com|Teacher ------------------------------------------------------
#代码如下
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
class Manager: OPERATOR_DIC =[ ("创建学生账号","create_student"), ("创建课程信息","create_course"), ("查看学生信息","check_student") ] def __init__(self,name): self.name = name def create_student(self): print("创建学生账号") def create_course(self): print("创建课程信息") def check_student(self): print("查看学生信息") class Student: OPERATOR_DIC = [ ("查看课程", "check_course"), ("选择课程", "choose_course"), ("查看已选课程", "choosed_course") ] def __init__(self,name): self.name = name def check_course(self): print("查看课程") def choose_course(self): print("选择课程") def choosed_course(self): print("查看已选课程") def login(): username = input("请输入用户名:") password = input("请输入密码:") with open("userinfo") as f: for line in f: user,passwd,ident = line.strip().split("|") if username == user and password == passwd: print("登录成功") return username,ident import sys def main(): usr,id = login() #login函数的返回值,即jianji Manager file = sys.modules['__main__'] #获取当前模板,即获取到当前文件的命名空间;相当于cls就是Manager类;如果是学生登录,cls就是Stuent类 cls = getattr(file,id) #等价于getattr(file,'Manager') obj = cls(usr) #实例化一个用户对象,相当于obj = Student(jianji) # print(usr,id) # print(cls) operator_dic = cls.OPERATOR_DIC while 1 : #通过如下代码,可适用类中的所有方法 for num,i in enumerate(operator_dic,1): print(num,i[0]) choice = int(input("请输入你的选择>>>")) choice_item = operator_dic[choice-1] #此处获取到的是一个字符串,只能使用getattr()的方式 getattr(obj,choice_item[1])() main() 输入输出: 请输入用户名:jianji 请输入密码:123456 登录成功 1 创建学生账号 2 创建课程信息 3 查看学生信息 请输入你的选择>>>1 创建学生账号
3.双下方法(内置方法)
补充:
类似【__名字__】的方法叫做类的特殊方法、也叫做双下方法、也叫内置方法、也叫魔术方法(magic_method)
类中的每一个双下方法都有它自己的特殊意义
1)【__call__】
#对象()就相当于调用__call__方法
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
class A: def __call__(self, *args, **kwargs): print("执行__call__方法") a = A() a() #执行__call__方法 A()() #和上面结果一样,相当于调用 __call__方法(即先实例化一个对象,再对) 输出: 执行__call__方法 执行__call__方法
#B类调用A类的__call__方法(很多源码这么用)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
class A: def __call__(self, *args, **kwargs): print("执行__call__方法") class B: def __init__(self,cls): self.a = cls() #②cls()就相当于A(),即创建了一个A类对象,赋值给本类的属性a self.a() #即通过A类的对象,调用call方法 B(A) #①把B当作参数传给A 输出: 执行__call__方法
2)【__len__】
补充:len()方法可计算列表、元组等数据类型的长度
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
list = [2,3,4,7,6,1,6] tup = (1,2,3,4,5) print(len(list)) print(len(tup)) 输出: 7 5
#len(obj)就相当于调用了这个对象的__len__方法
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
class list: list = [1,2,3,4,5] def __len__(self): print("执行len方法。。。") return 3 #__len__方法的return值就是len函数的返回值 li = list() print(len(li)) #len(对象)实际就相当于调用了obj的__len__方法 输出: 执行len方法。。。 3
总结:__len__方法的return值就是len函数的返回值;如果一个obj对象没有__len__方法,那么len函数就会报错;即要使用len(obj)方法,必须再类中有__len__方法
#可自己定义对象的长度如何计算
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
class list: list = [1,2,3,4,5] tup = (1,2,3) def __len__(self): return len(self.tup) li = list() 输出: 3
总结:内置函数和类的内置方法是有很大关系的
练习:写一个类,它有一个字符串类型的属性,要求可统计类的obj对象的长度,该对象的长度就是字符串的长度
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
class str_len: def __init__(self): self.s = "我爱北京天安门" def __len__(self): return len(self.s) s = str_len() print(len(s)) 输出: 7
3)【__new__】
引:__init__是一个初始化方法,它不是构造方法;__new__才是构造方法
补充:实例化一个对象的过程:
①先开辟一个属于对象的空间
②把对象的空间传给self,然后执行init方法
③将这个对象的空间返回给调用者
1)实例化一个对象时,类本身是不能开辟内存空间空间的;开辟内存空间是__new__方法;因为所有的类都继承了object类,而object类中有一个__new__方法
①即先执行本类的__new__方法开辟对象空间,如果本类没有,只能调用object的__new__方法开辟空间
②把对象的空间传给self,然后执行init方法
③将这个对象的空间返回给调用者
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
class A: def __new__(cls, *args, **kwargs): #此时还没有对象空间,所以只能传类空间 obj = object.__new__(cls) print("执行new方法") print(obj) #<__main__.A object at 0x00000224717C5EB8> return obj def __init__(self): print(self) #<__main__.A object at 0x00000224717C5EB8> print("执行init方法") a = A() 输出: 执行new方法 <__main__.A object at 0x00000224717C5EB8> <__main__.A object at 0x00000224717C5EB8> 执行init方法
总结:__new__方法在实例化对象之后,但是在__init__方法之前执行
4)【__str__】
4)【__str__】
#print()一个对象,相当于调用一个对象的__str__方法;没有自定义__str__方法仍然可以打印内容是因为object类中实现了。所以打印的内容是一个内存地址,保证打印不出错,但是object类却不知道自定义类中的属性,所以可通过自定义__str__方法实现
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
class Student: def __init__(self,name,age,sex): self.name = name self.age = age self.sex = sex def __str__(self): #打印对象,自动调用__str__方法 return "%s %s %s" %(self.name,self.age,self.sex) s1 = Student("阿狸","18","女") s2 =Student("泽拉斯","500","男") print(s1) print(s2) 输出: 阿狸 18 女 泽拉斯 500 男
#str(obj),就相当于执行obj.__str__方法(即str(obj)得到__str__的返回值)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
class Student: def __init__(self,name,age,sex): self.name = name self.age = age self.sex = sex def __str__(self): #打印对象,自动调用__str__方法 return "%s %s %s" %(self.name,self.age,self.sex) s = Student("阿狸","18","女") print(str(s)) #str(obj)相当于执行__str__然后得到其返回值
# '%s' % obj 也相当于执行__str__方法,并得到其返回值
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
class Student: def __init__(self,name,age,sex): self.name = name self.age = age self.sex = sex def __str__(self): #打印对象,自动调用__str__方法 return "%s %s %s" %(self.name,self.age,self.sex) s = Student("阿狸","18","女") print("%s" % s) #【'%s' % obj】就相当于执行__str__方法,并获取其返回值 输出: 阿狸 18 女
4.单例类
1)定义:如果一个类,从头到尾只能有一个实例,那么这个类就是一个单例类(即只有一块内存空间),所以需要通过__new__方法实现
#当程序走到__init__方法时,空间已经开辟完了,所以要在__init__方法前的__new__方法就要自己定义开辟的空间,而不是继承object类的__new__方法
#不管实例化多少次,所有的对象指向同一块内存空间,所以最后一次的值,就是最终的值
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
class Single: __ISINSTANCE=None def __new__(cls, *args, **kwargs): if not cls.__ISINSTANCE : cls.__ISINSTANCE = object.__new__(cls) return cls.__ISINSTANCE def __init__(self,name,age): self.name = name self.age = age s1 = Single("阿狸",18) s2 = Single("埃利亚",20) print(s1.name,s1.age) print(s2.name,s2.name) print(s1,s2) #是同一个内存空间 输出: 埃利亚 20 埃利亚 埃利亚 <__main__.Single object at 0x000001935FC4CF28> <__main__.Single object at 0x000001935FC4CF28>
总结:所有的双下方法,没有需要在外部直接调用的;而是通过一些其他的内置函数,特殊的语法,来自动的触发这些双下方法