元类

一.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内存地址相同

原文地址:https://www.cnblogs.com/lizeqian1994/p/10158477.html