zoukankan      html  css  js  c++  java
  • 面向对象之反射、元类

    1.反射

    反射指的是一个对象应该具备,可以检测,修改,增加自身属性的能力

    反射就是通过字符串操作属性

    涉及四个函数,这四个函数就是普通的内置函数,没有双下划线,与print等在使用方法上没有区别

    hasattr,getattr,setattr,delattr(他们的具体使用方法和作用见下列代码)

     1 class Person:
     2     def __init__(self,name,age):
     3         self.name = name
     4         self.age = age
     5 
     6 p = Person('sxc',18)
     7 
     8 if hasattr(p,'name'):  # 判断某个对象是否存在某个属性
     9     print(getattr(p,'name'))  # 把这个值当做返回值返回出来
    10 
    11 print(getattr(p,'gender',None))  # 如果没有这个值后面还可以赋予他一个默认值
    12 
    13 setattr(p,'id',177)  # 可以为该对象赋予新的属性
    14 print(p.id)
    15 
    16 delattr(p,'id')  # 也可以删除的对象的属性
    17 print(p.id)

    使用场景:

    反射其实就是对属性的增删查改,但是如果直接使用内置的__dict__来操作,语法繁琐,不好理解

    如果是另一方提供的,无法判断是否是我需要的属性和方法

     

    简单的框架设计

    框架就是已经实现了最基础的构架,就是所有项目都一样的部分已经完成了。反射被称为框架的基石,框架的设计者不可能提前知道使用者的对象到底是怎么设计的,所以使用者提供给框架的对象必须通过判断验证之后才能正常使用,判断验证就是反射要做的事

    需求:实现一个用于处理用户的终端指令的小框架

     1 # 框架部分
     2 def run(plugin):
     3     while True:
     4         cmd = input('请输入您的指令>>>:').strip()
     5         if cmd == 'exit':
     6             break
     7         #  无法判断传入的参数,需要加入反射加以判断
     8         if hasattr(plugin,cmd):  # 取出对应的指令
     9             func = getattr(plugin,cmd)
    10             func()  # 执行指令处理
    11         else:
    12             print('请输入正确的指令')
    13     print('退出程序')
    14 
    15 # 从系统配置文件中截取模块路径和类名
    16 module_path, class_name = settings.CLASS_PATH.rsplit('.',1)
    17 # 拿到模块
    18 mk = importlib.import_module(module_path)
    19 # 拿到类
    20 cls = getattr(mk,class_name)
    21 # 实例化对象
    22 obj = cls()
    23 # 调用框架
    24 run(obj)
    项目框架以及运行代码
    # 配置文件路径
    
    CLASS_PATH = 'lib.plugin.WinCmd'
     1 class WinCmd:
     2     def cd(self):
     3         print('进入文件')
     4 
     5     def delate(self):
     6         print('删库跑路')
     7 
     8     def dir(self):
     9         print('文件列表')
    10 
    11 
    12 class LinuxCmd:
    13     def cd(self):
    14         print('进入文件')
    15 
    16     def rm(self):
    17         print('删库跑路')
    18 
    19     def ls(self):
    20         print('文件列表')
    插件代码

    2.元类 metaclass

    用于创建类的类就叫做元类

    在python中万物皆对象,类也是对象

    对象是通过类实例化产生的,如果类也是对象的话,必然类对象也是由另一个类实例化产生的

    默认情况下所有类的元类都是type

    元类的验证:

    1 # 元类的验证
    2 class Person:
    3     pass
    4 
    5 p = Person()
    6 
    7 # type方法可以查看对象的类型
    8 print(type(p))
    9 print(type(Person))

    学习元类的目的:

    可以高度自定义一个类,具体到可以控制类的名字必须以固定的格式书写

    我们可以通过初始化方法,只要继承元类并覆盖其中的__init__方法就能实现需求

    案例,通过元类限制类的名字必须以大驼峰的方式命名

     1 # 继承元类可以高度定义一个子类
     2 class MyType(type):
     3     def __init__(self,cls,bases,dict):
     4         super().__init__(cls,bases,dict)
     5         if not cls.istitle():
     6             raise Exception('请小写')
     7 # 生成两个子类
     8 class Pig(metaclass= MyType):
     9     pass
    10 
    11 class duck(metaclass= MyType):
    12     pass

    Pig类可以通过,duck类并未大写,报错

     

    元类中call方法

    当调用类对象时会自动执行元类中的__call__方法,并将这个类本身作为第一个参数传入,以及后面的参数

    覆盖元类中的call之后,就无法产生对象.必须调用super().__call__来完成对象的创建,并返回其返回值

     1 # __call__是用来实例化对象返回值的
     2 class MyType(type):
     3     def __call__(self, *args, **kwargs):
     4         print('元类call')
     5         print(args)
     6         print(kwargs)
     7 
     8 class Person(metaclass=MyType):
     9     def __init__(self,name):
    10         self.name = name
    11 
    12 p = Person('sxc',age = 18)
    13 print(p)

    当我们覆盖了元类的__call__方法,他的返回值就变成了None,说明__call__是用来实例化对象传值的

    案例1:把子类中的所有属性都改为大写

     1 # 把子类的所有属性都改成大写
     2 class MyType(type):
     3     def __call__(self, *args, **kwargs):
     4         new_args = []  # 定义一个新列表
     5         for a in args:  # 循环输出旧列表中的内容
     6             new_args.append(a.upper())  # 添加到新列表中
     7         return super().__call__(*new_args,**kwargs)
     8 
     9 
    10 class Person(metaclass=MyType):
    11     def __init__(self,name):
    12         self.name = name
    13 
    14 
    15 p = Person('sxc')
    16 p1 = Person('aabbcceeff')
    17 print(p.name)
    18 print(p1.name)

    案例2:要求创建对象时只能关键字传参

     1 # 要求:只允许关键字传参
     2 class MyType(type):
     3     def __call__(self, *args, **kwargs):  # 覆盖元类的__call__方法
     4         if args:
     5             raise Exception('不允许直接传参')  # 当直接传参时报错
     6         return super().__call__(*args,**kwargs)
     7 
     8 class Person(metaclass= MyType):
     9     def __init__(self,name,age):
    10         self.name = name
    11         self.age = age
    12 
    13 p1 = Person(name = 'sxc',age = 18)
    14 print(p1.name,p1.age)
    15 p = Person('sxc',18)

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

    __init__方法和__call__方法的使用场景:

      当想要控制对象的创建过程时,就覆盖call方法

      当想要控制类的创建过程时,就覆盖init方法

    补充new方法

    当创建类对象时,会首先窒息感元类中的__new__方法,拿到一个空对象,然后自动调用__init__来初始化

     1 # new方法
     2 class Mytype(type):
     3     def __new__(cls, *args, **kwargs):
     4         print(cls)  # 元类自己
     5         print(args)  # 创建类需要的几个参数  类名,基类,名称空间
     6         print(kwargs)  # 空的值
     7         print('new run')
     8         return super().__new__(cls, *args, **kwargs)
     9 
    10     def __init__(self,a,b,c):
    11         super().__init__(a,b,c)
    12         print('init run')
    13 
    14 class A(metaclass= Mytype):  # new方法和call方法在类生成时自动执行
    15     pass
    16 
    17 print(A)  # 类对象本身

    注意:当我们覆盖了__call__方法必须保证new方法有返回值并且是对应的类对象

     

    单例设计模式

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

    单例实现

     1 # 单例的实现
     2 class Single(type):
     3     def __call__(self, *args, **kwargs):
     4         if hasattr(self,'obj'):  # 判断是否有已存在的对象
     5             return getattr(self,'obj')  # 有就返回
     6         obj = super().__call__(*args,**kwargs)  # 没有就创建
     7         self.obj = obj  # 存到类中,方便下次查找
     8         return obj
     9 
    10 class Person(metaclass=Single):
    11     def __init__(self,name):
    12         self.name = name
    13 
    14 # 只会创建初始的对象
    15 p = Person('sxc')
    16 print(p.name)
    17 p1 = Person('zzj')
    18 print(p1.name)
    19 p2 = Person('zzp')
    20 print(p2.name)
    21 p3 = Person('lzx')
    22 print(p3.name)
    23 p4 = Person('zkj')
    24 print(p4.name)

    元类练习:

     1 '''
     2 1.使用元类实现检测类中的所有属性 包括类属性和方法,将名字存储到类的名称空间中
     3 例如:有student类  有eat函数 和school属性  ,获取这两个名字 存储到一个叫做attrs的列表中,并将列表作为student类的属性
     4 使得student类可以通过访问attrs来获取自身所有的属性名字
     5 '''
     6 class MyType(type):
     7     def __init__(self,cls,bases,dict):
     8         attr = []
     9         for i in dict:  # 遍历字典中的类属性
    10             if i == 'school' or i == 'eat':
    11                 attr.append(i)  # 把他添加到新的列表中去
    12         self.attr = attr  # 添加到类属性中
    13         super().__init__(cls,bases,dict)
    14 
    15 class Student(metaclass=MyType):
    16     school = 'oldboy'
    17 
    18     def eat(self):
    19         print('吃东西')
    20 
    21 stu = Student()
    22 print(stu.attr)
    练习

     24

  • 相关阅读:
    win7 64位系统,vs2010下配置OpenGL开发环境
    OpenCV stereo matching 代码 matlab实现视差显示
    Cocos2d-x 3.x游戏开发之旅
    芯片验证漫游指南
    名师讲坛:PHP开发实战权威指南
    Python带我起飞:入门、进阶、商业实战
    新编Excel会计与财务管理应用大全(2016实战精华版)
    CorelDRAW X7中文版完全自学宝典
    HTML5 canvas开发详解(第2版)
    中文版3ds Max 2014--VRay效果图制作实用教程
  • 原文地址:https://www.cnblogs.com/sxchen/p/11272885.html
Copyright © 2011-2022 走看看