zoukankan      html  css  js  c++  java
  • 元类

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

  • 相关阅读:
    从清月高中物理动学课件制作工具说【FarseerPhysics引擎之WheelJoint】及【PropetryGrid之动态下拉列表】
    一种从纹理图片提取多边形的方法
    蒸汽世界满手尘土生命、水、光照锁定修改器
    【五子棋AI循序渐进】——整合完成
    洛谷-07敦刻尔克大撤退-[再也不坑]【二战2】二战系列2:狼烟四起
    洛谷-火柴棒等式-NOIP2008提高组复赛
    洛谷-笨小猴-NOIP2008提高组复赛
    NOIP2013-普及组复赛-第一题-计数问题
    NOIP2010-普及组复赛-第四题-三国游戏
    NOIP2012-普及组复赛-第二题-寻宝
  • 原文地址:https://www.cnblogs.com/lizeqian1994/p/10158477.html
Copyright © 2011-2022 走看看