反射 元类

Posted zrh-960906

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了反射 元类相关的知识,希望对你有一定的参考价值。

1.反射 reflect

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

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

涉及四个普通的内置函数,没有双下划线,

hasattr    getattr     setattr      delattr

class Person:
    def __init__(self,name,age,male):
        self.name = name
        self.age = age
        self.male = male
p = Person(jack,18,man)
if hasattr(p,"name"):     # 判断某个对象是否存在某个属性
    print(getattr(p,name,None))    # jack  # 从对象中取出属性,第三个值位默认值 当属性不存在是返回默认值
setattr(p,id,123)
print(p.id)          #123
delattr(p,id)
print(p.id)

使用场景:

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

另外一个最主要的问题是,如果对象不是我自己写的是另一方提供的,我就必须判断这个对象是否满足的要求,也就是是否我需要的属性和方法

框架代码:

反射被称为框架的基石,为什么
因为框架的设计者,不可能提前知道你的对象到底是怎么设计的
所以你提供给框架的对象 必须通过判断验证之后才能正常使用
判断验证就是反射要做的事情,
当然通过__dict__也是可以实现的, 其实这些方法也就是对__dict__的操作进行了封装
需求:要实现一个用于处理用户的终端指令的小框架
框架就是已经实现了最基础的构架,就是所有项目都一样的部分

import importlib
import settings
def run(plugin):
    while True:
        cmd = input(请输入指令).strip()
        if cmd == exit:
            break
        if hasattr(plugin,cmd):  # 取出对应方法
            func = getattr(plugin,cmd)
            func()
        else:
            print(该指令不支持)
    print(see you la la)
path = settings.CLASS_PATH
module_path,class_name = path.rsplit(.,1)  # 从配置中单独拿出来 模块路径和 类名称
mk = importlib.import_module(module_path)   #拿到模块
cls = getattr(mk,class_name)  # 拿到类
obj = cls()  # 实例化对象
run(obj)   #调用框架

settings 文件里的代码为
CLASS_PATH = ‘plugins.LinuxCMD‘
plugins 文件里的代码
class WinCMD:
def cd(self):
print(‘wincmd 切换目录‘)
def delete(self):
print(‘wincmd 删文件‘)
def dir(self):
print(‘wincmd 列出文件‘)
class LinuxCMD:
def cd(self):
print(‘Linuxcmd 切换目录‘)
def rm(self):
print(‘Linuxcmd 删文件‘)
def ls(self):
print(‘Linuxcmd 列出文件‘)
class Person(object):
    name = 123
p = Person()
print(type(p))    # <class ‘__main__.Person‘>
print(type(Person))   <class ‘type‘>

直接调用type类来产生类对象

一个类的三个基本组成部分

1.类的名字  2.类的父类们   3.类的名称空间

cls_obj = type(‘dog‘,(),)
print(cls_obj) # <class ‘__main__.dog‘>

元类 metaclass

用于创建类的类, 对象是通过类实例化产生的,如果类也是对象的话,必然类对象也是有另一个类实例化产生的

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

学习元类的目的:

高度的自定义一个类,例如控制类的名字必须以大驼峰的方式来书写

类也是对象,也有自己的类,

我们的需求是创建类对象做一些限制

想到了初始化方法 我们只要找到类对象的类(元类),覆盖其中 init方法就能实现需求

当然我们不能修改源代码,所以应该继承type来编写自己的元类,同时覆盖init来完成需求

class MyType(type):    # 定义了一个元类
    def __init__(self,clss_name,bases,dict):
        super().__init__(clss_name,bases,dict)
        if not clss_name.istitle():
            raise Exception(类名有误)
class Pig(metaclass = MyType):    # 为pig类指定了元类为MyType
    pass
class MyType(type):
    def __call__(self, *args, **kwargs):
        new_args = []
        for a in args:
            new_args.append(a.upper())
        print(new_args)   # [HAHA]
        print(kwargs)    # name: jack, gender: woman
        return super().__call__(*new_args,**kwargs)
class Person(metaclass=MyType):
    def __init__(self,*args,name,gender):
        self.name = name
        self.gender = gender
p = Person(haha,name="jack",gender="woman")
print(p.name)   #jack
print(p.gender)   #woman
要求创建对象时,必须以关键字参数形式传参
覆盖元类的__call__
判断你有没有传非关键字参数 == 不能有位置参数, 有就炸
class
Mate(type): def __call__(self, *args, **kwargs): if args: raise Exception(不允许使用位置参数) return super().__call__(*args,**kwargs) class A(metaclass=Mate): def __init__(self,name): self.name = name a = A(name=jack) print(a.name) # jack

元类中call方法

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

使用场景:

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

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

补充new方法

当你要创建类对象时,会首先执行元类中的__new__方法,拿到一个空对象,然后会自动调用__init__来对这个类进行初始化操作  
注意:,如果你覆盖了该方法则必须保证,new方法必须有返回值且必须是,对应的类对象

class Meta(type):
    def __new__(cls, *args,**kwargs):
        obj = type.__new__(cls,*args,**kwargs)
        return obj
    def __init__(self,a,b,c):
        super().__init__(a,b,c)
        print(init run)
class A(metaclass=Meta):    # init run
    pass
print(A)    #  <class ‘__main__.A‘>
class Single(type):
    def __call__(self, *args, **kwargs):
        if hasattr(self,obj):    #判断是否存在已经有的对象
            return getattr(self,obj)
        obj = super().__call__(*args,*kwargs)  # 没有则创建
        print(new 了)
        self.obj = obj   # 并存入类中
        return obj
class Studenet(metaclass= Single):
    def __init__(self,name):
        self.name = name
class Person(metaclass=Single):
    pass
Person()
Person()

单例设计模式: 指的是一个类产生一个对象

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

 

以上是关于反射 元类的主要内容,如果未能解决你的问题,请参考以下文章

反射,元类

反射 元类

反射与元类

反射 动态导入 元类

OOP 反射&元类

反射元类 练习