• 面向对象之反射,元类


    一.反射

    1.什么是反射

    反射的概念是由Smith在1982年首次提出的,主要是指程序可以访问,检测和修改它本身状态或行为的一种能能力(自省)。

    2.python中的反射

    反射是所有面向对象编程语言都具备的功能

    python中通过一下四个函数来实现反射

    1 hasattr(object,name)  #判断对象是否拥有某个属性
    2 setattr(object,name,value) #为对象增加新的属性
    3 getattr(object,name,default) #从对象中获取某个属性
    4 delattr(object,name) #从对象中删除某个属性

    3.为什么需要反射

    一个类在定义的时候,可能一些属性的设计并不是很完美,而后期需要做出修改,或是增加新属性时,使用反射的方式可以不需要修改源代码

      反射的另一个优势(重点):可插拔设计 

        不仅限于修改已有的属性,通过反省能够发现已存在的属性,只要你给我一个对象我就能检查其拥有的属性,从而使用这些属性,而不需要提前了解这些对象,这大大提高了程序的扩展性

    案例1:

     1 import plugins
     2 def run(plugin):
     3     while True:
     4         cmd = input('请输入指令:').strip()
     5         if cmd == 'exit':
     6             break
     7         if hasattr(plugin,cmd):
     8             fun = getattr(plugin,cmd)
     9             fun()
    10         else:
    11             print('该指令不受支持')
    12       print(‘已退出’)
    1 #plugins
    2 
    3 class Wincmd:
    4         def dir(self):
    5             print('wincmd列出所有文件。。。')
    6         def cd(self):
    7             print('wincmd 切换目录。。。')
    8         def delete(self):
    9             print('wincmd 要不要删除目录。。。 ')

    对案例1进行改进,使用动态导入模块

    案例2:

      1.首先配置settings

    class_path = 'lib.plugins.Wincmd'

      2.主体框架:

     1 import  importlib
     2 from 面向对象 import settings
     3 def run(plugin):
     4     while True:
     5         cmd = input('请输入指令:')
     6         if cmd == 'exit':
     7             break
     8         if hasattr(plugin,cmd):
     9             func = getattr(plugin,cmd)
    10             func()
    11         else:
    12             print('该指令不受支持')
    13     print('see you la la')
    14 
    15 path,class_name = settings.class_path.rsplit('.',1)  
    16 mk = importlib.import_module(path)     #找到模块(文件)
    17 cls = getattr(mk,class_name)  #通过反射,模块与类名,得到类
    18 obj = cls()  #实例化对象
    19 run(obj)  #调用函数  传入对象

    二.元类

    1.什么是元类?

    在Python中,一切皆对象,类也是对象。

    验证一下:

    1 class Person:
    2     pass
    3 print(type(Person))
    4 #<class 'type'>

    由此看来Person类属于type类,是由type类产生的对象。

    因此可以推导出:

    用于实例化产生类的类称之为元类,就是此时的type类

    2.创建类的流程分析:

     一个类有三个基本组成部分:

      1.类的名字(字符类型)

      2.类的父类们(是一个元组或列表)

      3.类的名称空间(字典类型)

    用另一种方式出创建类

    cls_obj = type('dog',(),{})
    #同样可以创建类

    3.自定义元类,通过自定义原来创建类

    创建类是由type完成的type中必然包含了创建了的具体代码,现在需要对这些代码进行修改,两种方式:

      1.修改type源代码不可取

      2.创建新的元类,使用自己的元类来创建类从而实现定制类

    一个类没有声明自己的元类,默认他的元类就是type,除了使用内置元类type,我们也可以通过该继承type

    来自定义元类,然后使用metaclass关键字参数为一个类指定元类

    1 class MyType(type):
    2     def __init__(self,clss_name,bases,dict):
    3         super().__init__(clss_name,bases,dict)
    4 
    5 class Person(metaclass = MyType):
    6     pass

    4.在自定义元类中通过覆盖type类中的__init__来控制定义类

    下面有个需求,我们高度自定义一个类,需要控制类的名字必须以大驼峰的方式来书写

    这是需要在自定义元类中覆盖type类中的__init__方法。

    class MyType(type):
        def __init__(self,cls_name,bases,dict):
            super().__init__(cls_name,bases,dict)
            if not cls_name.istitle():
                raise Exception('类名要大写的驼峰')
    
    class Pig(metaclass = MyType):
        pass

    5.元类中call方法(当你想要控制对象的创建过程时)

    当你调用类对象时会自动珍惜元类中的__call__方法,并将这个类本身作为第一个参数传入,以及右面的一堆参数,覆盖元类中的call之后,这个类就无法产生对象,必须调用super().__call__来完成对象的创建,并返回其返回值。

    案例:实现将对象的所有属性名成转为大写

    class MyType(type):
        def __call__(self,*args,**kwargs):
            for k,v in kwargs.items():
                kwargs[k] = v.upper()
            return super().__call__(*args,**kwargs)
    
    class Person(metaclass = MyType):
            def __init__(self,name,gender):
                self.name = name
                self.gender = gender
    p = Person(name = 'jack',gender = 'man')
    print(p.name)
    print(p.gender)

    注意:一旦覆盖了call必须调用父类的call方法来产生对象并返回这个对象

    6.单例设计模式

    单例:指的是一个类产生一个对象

    为什么要使用单例:单例是为了节省资源,当一个类的所有对象属性全部相同时,则没有必要创建多个对象

    需求:只生成一个对象

     1 class Single(type):
     2     def __call__(self, *args, **kwargs):
     3         if hasattr(self,"obj"): #判断是否存在已经有的对象
     4             return getattr(self,"obj") # 有就返回
     5         obj = super().__call__(*args,**kwargs) # 没有则创建
     6         self.obj = obj # 并存入类中
     7         return obj
     8 
     9 class Person(metaclass=Single):
    10     pass
    11 
    12 # 只会创建一个对象
    13 Person()
    14 Person()
    15 #创建再多个对象都是同一个
    万般皆下品,唯有读书高!
  • 相关阅读:
    IBM ThinkPad SL400 XP驱动
    IMAIL系统修改IP地址的处理方法
    微信小程序setData的回调方法
    数据库的事务常识
    Java中的多线程
    微信小程序合并两个json对象
    微信小程序setData修改对象的属性或者数组中的某个对象的属性
    索引常识
    并发与并行的区别
    避免问题发生的代码规范
  • 原文地址:https://www.cnblogs.com/s686zhou/p/11271154.html
Copyright © 2020-2023  润新知