一.isinstance issubclass
class Person:
pass
class Student(Person):
pass
stu1=Student()
#判断是不是实例
print(isinstance(stu1,Student))
#判断是不是子类
print(issubclass(Student,Person))
二.反射
反射 实际上就是反省
简单来说就是 对象具有一种修正错误的能力
hasatter 是否存在某个属性
getatter 获取某个属性
setatter 设置某个属性
delatter 删除某个属性
这几个方法有个共同点都是通过字符串来操作属性
通过字符串来操作属性 ,这就是反省
class Student:
def __init__(self,name,sex,age):
self.name=name
self.sex=sex
self.age=age
def study(self):
print('该学生正在学习')
stu1=Student('henry','man',29)
def test(obj):
if hasattr(obj, 'name'):
print(getattr(obj, 'name'))
else:
print('没有name这个属性')
test(stu1)
这个test方法可以直接由print(getattr(stu1,"name","没有name属性")) 替换
setattr(stu1,'school','shandong')
print(getattr(stu1,'school')) #结果:shandong
delattr(stu1,'school')
print(getattr(stu1,'school','没有school这个属性')) #结果:没有school这个属性
什么时候用反射?
如果在编写代码期间 就能明确知道我要访问的属性 没有必要使用反射
如果在编写代码期间 无法明确知道我要访问的属性 这时就应该使用反射
class Student:
def study(self):
print('正在学习中')
stu=Student()
res=getattr(stu,'study',None)
print(res) #绑定的方法 bound method
def eat():
print('正在吃东西')
setattr(stu,'eat',eat)
print(getattr(stu,'eat',None)) #function
可以通过反射的方式为对象增加一个方法 但是注意 这样增加的方法就是一个普通函数 不会自动传值
需要编写一个CMD工具 这个工具可以支持两个命令 dir ,tasklist
class CMD:
def dir(self):
print('查看当前文件夹目录')
def tasklist(self):
print('查看任务列表')
cmd=CMD()
res=input('请输入指令:').strip()
if hasattr(cmd,res):
func=getattr(cmd,res)
func()
else:
print('输入指令有误')
三.__str__ __del__
前后带__的都是特殊的内置函数,会在某些时机自动执行,一般情况我们不应该直接调用它们
当我们需要自定义打印现实内容时 就需要__str__方法
该方法必须返回一个字符串
class Test:
def __init__(self,name):
self.name=name
def __str__(self):
print('str run..')
return self.name
t=Test('henry')
print(t)
结果:str run..
henry
# 在讲一个对象转换字符串时 本质就是在调用这个对象 __str__方法
print(str(t))
__del__
当对象被从内存中删除时会自动执行
另一种情况时 程序员手动删除了这个对象 也会自动执行
什么时候使用__del__
在python中 有自动内存管理机制 所以 python自己创建的数据 不需要我们做任何操作
但是有一种情况 我们使用python打开了一个不属于python管理的数据
比如打开了一个文件 这个文件一定是操作系统在打开 会占用系统内存 而python解释器无法操作系统内存的
所以 当你的python解释器运行结束后 文件依然处于打开状态 这时候就需要使用__del__来关闭系统资源
简单地说 当程序运行结束时 需要做一些清理操作 就使用__del__
__del__也称之为 析构函数
分析构造 并拆除这个对象
class Testfile:
def __init__(self,filename,mode,encoding='utf-8'):
self.file=open(filename,mode,encoding=encoding)
def read(self):
return self.file.read()
def wtite(self,text):
self.wtite(text)
def __del__(self):#该方法其实就是一个通知性质,仅仅告诉程序员对象将被删除
self.file.close() #在这里关闭文件
tf=Testfile('E:python-li课堂day26测试文件','rt')
print(tf.read())
四.exec
exec
execute的缩写
表示执行的意思
作用:帮助解析执行的代码,并且将得到的名称存储到指定的名称空间 解释器的内部也是调用它来执行代码的
三个参数:
参数一:需要一个字符串对象,表示需要被执行的python语句
参数二:是一个字典 表示全局名称空间
参数三:也是一个字典 表示局部名称空间
#如果同时制定了 全局和局部 则 会将字符串中包含名称 解析后存到局部中
globalsdic={}
localsdic={}
exec('''
aaaaaa=1
bbbbb=1
def func1():
print('我是func1')
''',globalsdic,localsdic)
print(localsdic)
#结果:{'aaaaaa': 1, 'bbbbb': 1, 'func1': <function func1 at 0x000001FBA4AF1E18>}
#如果只传了一个传参数 则 将字符串中包含名称 解析后存到全局中
dic={}
exec('''
aaaaa=1
bbbbb=1''',dic)
print(dic)#输出的就是全局的
exec中,放到第二个参数位置的字典不论名字是什么都表示全局名称空间,放到第三个位置的字典不论名字是什么都表示局部名称空间.
五.元类
1.什么是元类?
一切皆对象
class关键字自定义的类其实也是一个对象
元类是指产生类的类 type就是元类
所有的自定义的类都是通过type实例化得来的
2.为何要用元类?
为了控制类的产生过程,还可以控制对象的产生过程
3.怎么用?
创建元类方法有两种?
方式一:用class关键字去创建,用的默认的元类type
class Student:
school='beijing'
def __init__(self,name,age):
self.name=name
self.age=age
def study(self):
print('正在学习!')
s1=Student('henry','23')
print(type(s1))#结果:<class '__main__.Student'> s1的类是Student
print(type(Student)) #结果:<class 'type'> Student的类是type,类其实是type类型的实例(对象)
print(Student) #结果:<class '__main__.Student'>
方式二:用自定义元类
首先要明白创建类的三要素 类名,类的父类,类的名称空间
类名:
class_name='Student'
类的父类也叫基类:
class_bases=(object,) #其实是一个元组
类的名称空间字典的形式:
class_dic={}
class_body='''
school='beijing'
def __init__(self,name,age):
self.name=name
self.age=age
def study(self):
print('正在学习!')
'''
exec(class_body,{},class_dic) #利用exec传入名称空间
#准备好创建类的三要素
print(class_name)
print(class_bases)
print(class_dic)
#Student=type(类名,基类,类的名称空间)
Student=type(class_name,class_bases,class_dic)
print(Student) #<class '__main__.Student'>
class Test(object): #Test = type("Test",(object,),{})
pass
自定义元类的目的
目的一.自定义元类通过__call__方法控制类的调用过程及控制对象实例化过程
__call__ :调用的意思,在对象被调用时执行
class Mymeta(type):
def __call__(self, *args, **kwargs):
print('Mymeta中的call run')
print(self) #<class '__main__.Student'>
print(args) #('henry', 29)
print(kwargs) #{}
class Student(object,metaclass=Mymeta):
def __init__(self,name,age):
self.name=name
self.age=age
def study(self):
print('正在学习!')
obj=Student('henry',29)
调用Student的目的:
先创建一个Student的空对象
为该空对象初始化所有的属性
#自定义一个元类,需要继承type
class Mymeta(type):
#self 要创建对象的那个类(Student) *调用Student类时传入的参数
def __call__(self, *args, **kwargs):
#固定写法
#创建一个空对象
obj = self.__new__(self)
#为空对象初始化独有的属性
self.__init__(obj,*args,**kwargs)
#返回一个初始好的对象,得到一个完整的对象
return obj
#修改Student的元类为Mymeta
class Student(object,metaclass=Mymeta):
def __init__(self,name,age):
self.name=name
self.age=age
def study(self):
print('正在学习!')
#调用Student这个对象时 执行的是 Student的类(type)中__call__ 方法
obj=Student('henry',29)
print(obj.name)
print(obj.age)
obj.study()
目的二:控制类的创建过程
要控制类的创建过程 只要找到类所属的类 中的__init__即可
class Mymeta(type):
#self 刚建出来的类
#第二个 类的名字
#第三个 类的父类们 元组的形式
#第四个 这个类传进来的名称空间
def __init__(self,class_nane,bases,namespace):
#控制类的名称必须大写
if not class_nane.istitle():
#该代码是主动抛出异常
raise TypeError('类名开头必须大写')
#控制刚建的类必须有__doc__这个属性
if not self.__doc__:
raise TypeError('类中必须由文档注释')
class Student(object,metaclass=Mymeta):
'''
这是文档注释 可以通过__doc__来获取
这是一个学生类
'''
def __init__(self,name,age):
self.name=name
self.age=age
def study(self):
print('正在学习!')
元类使用总结:
元类是用于创建类的类
学习元类是为了 能控制类的创建过程 以及 类实例化对象的过程
一.
控制类的创建过程
1.创建一个元类 (需要继承type)
2.覆盖__init__方法 该方法 会将新建的类对象 类名 父类们 名称空间 都传进来 ,
可以利用这些信息在做处理
3.对于需要被控制的类 需要指定metaclass 为上面的元类
二.
控制类实例化对象的过程
1.创建一个元类 (需要继承type)
2.覆盖__call__方法 会将 正在实例化对象的类 调用类是传入的参数 都传进来
3.在__call__方法中 必须要先编写模板代码
3.1创建空对象
3.2调用类的__init__方法来初始化这个空对象
3.3返回该对象
4.加入你需要控制的逻辑
类的三个组成部分
类名 父类们 名称空间
元类 -> 实例化产生 -> 类 -> 实例化产生 -> 对象
六.单例
单例:
单例模式是一种设计模式
单个实例
一个类如果只有一个实例 那么这个类被称之为单例
什么时候使用单例?
当要处理的对象只有一份时或者当所有对象的属性都相同时
打印机类:实现单例
方法一:
class Printer:
'''
这是一个单例,请不要直接实例化,由get方法来获取实例
'''
obj=None
def __init__(self,name,brand,type):
self.name=name
self.brand=brand
self.type=type
def printing(self,test):
print('正在打印%s'%test)
@classmethod
def get_printer(cls):
if not cls.obj:
cls.obj=cls('Es005','爱普生','彩色打印机')
return cls.obj
else:
return cls.obj
p=Printer.get_printer()
print(p)
'''
通过上述方法来获取对象可以保证只有一个对象
但是这还不够,因为我们仍然可以通过调用类来产生对象
'''
方法二:
'''
就应该使用元类 来控制实例化的过程 __call__
在__call__ 中编写代码 保证每次调用call 都返回同一个实例 即可
'''
class Mymeta(type):
obj=None
def __call__(cls,*args,**kwargs):
if not Mymeta.obj:
obj=object.__new__(cls)
cls.__init__(obj,*args,**kwargs)
Mymeta.obj=obj
return Mymeta.obj
else:
return Mymeta.obj
class Printer(object,metaclass=Mymeta):
def __init__(self,name,brand,type):
self.name=name
self.brand=brand
self.type=type
def printing(self,test):
print('正在打印%s'%test)
p1=Printer('傻子','服气','无聊')
p2=Printer('你说','我不说','可怜')
print(p1)
print(p2)
p1和p2内存地址相同