反射 元类

反射 refiect

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

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

涉及的四个内置函数

hasattr(对象名, 属性名) : 判断某个对象是否存在某个属性
getattr(对象名, 属性名, 默认值) : 从对象中取出属性,第三个值是默认值,当属性不存在时返回默认值
setattr(对象名, 属性名, 属性值) : 为对象增加新的属性和值
delattr(对象名, 属性名) : 删除对象的属性
class A:
    def __init__(self, name, age, gander):
        self.name = name
        self.age = age
        self.gander = gander
    def hai(self):
        print('hai')
​
a = A('x', 20, 'man')
​
# 判断某个对象是否存在某个属性
if hasattr(a, 'name'):
    # 从对象中取出属性,第三个值是默认值,当属性不存在时返回默认值
    print(getattr(a, 'name', None))
# 为对象增加新的属性和值
setattr(a, 'id', 110)
print(a.id)
# 删除对象的属性
delattr(a, 'id')
​
if hasattr(a, 'id'):
    print(a.id)
else:
    print('没有 id')
    
print(getattr(a, 'hai')) 
# >>>  <bound method A.hai of <__main__.A object at 0x00000209A0549C18>>
func = getattr(a, 'hai')
func()  # >>> hai

应用场景:

反射其实就是对一个对象属性的增删改查,也可以通过通过__dict__来操作,单语法繁琐

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

框架设计方式:

'''
反射被称为框架的基石
框架的设计值,不可能提前知道你的对象到底是怎么设计的,所以你提供给框架的对象必须通过判断验证之后才能正常使用,判断验证就是反射要做的事情
'''
'''插件部分'''
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_PATH = 'libs.plugins.LinuxCMD'
        
'''框架部分'''from frame import settings
import importlib
​
​
def run(obj):
    while True:
        cmd = input('请输入指令:')
        if cmd == 'exit':
            break
        # 因为无法确定框架使用者是否传入正确的对象,所以需要使用反射来检测
        # 判断对象是否具有处理这个指令的方法
        if hasattr(obj, cmd):
            # 取出该方法
            func = getattr(obj, cmd)
            # 执行该方法
            func()
        else:
            print('该指令不支持')
    print('退出')
​
​
# 框架得根据配置文件拿到需要的类
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)
​
# 如此一来 框架就与现实代码彻底解耦了

元类 metaclass

type 就是所有类的元类

元类是用于创建类的类

万物皆对象,类也是对象

对象时通过类实例化产生的,所以类对象必然也是由另一个类实例化产生的

class A:
    pass
a = A()
print(type(a))  # <class '__main__.A'>
print(type(A))  # <class 'type'>
# A类是通过type类实例化产生的

学习元类的目的:

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

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

class MyType(type):
    # 覆盖元类中的__init__方法
    def __init__(self, cls_name, bases, dict):
        # 调用元类中的__init__方法,使用其初始化功能
        super().__init__(cls_name, bases, dict)
        # 添加判断条件
        if not cls_name.istitle():
            raise Exception('首字母需要大写')
​
class A(metaclass=MyType):
    pass
print(type(A))

元类中的call方法

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

使用场景:

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

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

案例:

实现将对象的所有属性名称大写:

lass MyType(type):
    def __call__(self, *args, **kwargs):
        new_args = []
        for a in args:
            new_args.append(a.upper())
​
        print(new_args)
        print(kwargs)
        # 注意: 一旦覆盖了__call__ 必须调用父类的call方法来产生对象并返回这个对象
        return super().__call__(*new_args,**kwargs)
​
​
class Person(metaclass=MyType):
    def __init__(self,name,gender):
        self.name = name
        self.gender = gender
​
p = Person(name="jack",gender="woman")
print(p.name)
print(p.gender)

new方法

当你要创建对象时,会首先执行元类中的__new__方法,拿到一个空对象,然后会自动调用__init__来对这个类进行初始化操作
注意:如果你覆盖了该方法则必须保证__new__方法必须有返回值,且必须是对应的类对象
class Meta(type):
​
    def __new__(cls, *args, **kwargs):
        print(cls) # 元类自己
        print(args) # 创建类需要的几个参数  类名,基类,名称空间
        print(kwargs) #空的 
        print("new run")
        # return super().__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):
    pass
print(A)

__new__方法和__init__都可以实现控制类的创建过程,__init__更简单

单例设计模式

用于解决某种问题的固定思路
例如:MVC MTV等
单例:指的是一个趔产生一个对象
目的:单例是为了节省资源,当一个类的所有对象属性全部相同时,则没有必须要创建多个对象
# 单例n元类
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 Student(metaclass=Single):
    def __init__(self,name):
        self.name = name
​
​
class Person(metaclass=Single):
    pass# 只会创建一个对象
Person()
Person()

 

 

posted @ 2019-07-30 18:49  waller  阅读(213)  评论(0编辑  收藏  举报