d24 反射,元类

反射

反省,自省

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

四个函数:

hasattr  判断某个对象是否在某个属性
getattr  从对象中取出属性,第三个值为默认值,当属性不存在时,返回默认值
setattr   为对象添加新的属性
delattr      删除属性

例子:

class Person():
    def __init__(self,name,age,height):
        self.name = name
        self.age = age
        self.height = height

p = Person('lala',16,158.5)
# print(p.name)

# print(hasattr(p,'name'))
if hasattr(p,'name'):
    # 判断某个对象是否存在某个属性
    print(getattr(p,'name'))
    # 从对象中取出属性,第三个值位默认值 当属性不存在是返回默认值
    print(getattr(p,'age',None))

#添加新的属性
setattr(p,'id','162')
print(p.id)

# 从对象中删除属性
delattr(p,'id')
print(p.id)
View Code

使用场景

反射被称为框架的基石,为什么
因为框架的设计者,不可能提前知道你的对象到底是怎么设计的
所以你提供给框架的对象 必须通过判断验证之后才能正常使用
判断验证就是反射要做的事情,
当然通过__dict__也是可以实现的, 其实这些方法也就是对__dict__的操作进行了封装

需求:

:要实现一个用于处理用户的终端指令的小框架
框架就是已经实现了最基础的构架,就是所有项目都一样的部分

使用场景:

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

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

案例:

import plugins

# 框架已经实现的部分
def run(plugin):
    while True:
        cmd = input("请输入指令:")
        if cmd == "exit":
            break
        # 因为无法确定框架使用者是否传入正确的对象所以需要使用反射来检测
        # 判断对象是否具备处理这个指令的方法
        if hasattr(plugin,cmd):
            # 取出对应方法方法
            func = getattr(plugin,cmd)
            func() # 执行方法处理指令
        else:
            print("该指令不受支持...")
    print("see you la la!")


# 创建一个插件对象 调用框架来使用它
# wincmd = plugins.WinCMD()
# 框架之外的部分就有自定义对象来完成
linux = plugins.LinuxCMD()
run(linux)


插件部分:


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 列出所有文件....")



上述框架代码中 写死了必须使用某个类,这是不合理的,因为无法提前知道对方的类在什么地方 以及类叫什么

所以我们应该为框架的使用者提供一个配置文件,要求对方将累的信息写入配置文件 

然后框架自己去加载需要的模块

最后的框架代码:




import importlib
import settings

# 框架已经实现的部分
def run(plugin):
    while True:
        cmd = input("请输入指令:")
        if cmd == "exit":
            break
        # 因为无法确定框架使用者是否传入正确的对象所以需要使用反射来检测
        # 判断对象是否具备处理这个指令的方法
        if hasattr(plugin,cmd):
            # 取出对应方法方法
            func = getattr(plugin,cmd)
            func() # 执行方法处理指令
        else:
            print("该指令不受支持...")
    print("see you la la!")


# 创建一个插件对象 调用框架来使用它
# wincmd = plugins.WinCMD()
# 框架之外的部分就有自定义对象来完成

# 框架 得根据配置文件拿到需要的类

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)
View Code

动态导入

"""
在框架设计中 我们不可能提前知道 框架的用户要提供类相关的信息
"""


# 动态导入
import importlib


# 拿到模块
p1 = importlib.import_module('libs.plugins')
print(p1)

# 从模块中取出类
cls = getattr(p1,'WinCMD')
print(cls)

# 实例化产生对象
obj = cls()
obj.cd()
类名称,属性名称,模块路径要保证大小写一致

给myframework补充(已经实现的方法)

import importlib
from libs import plugins
import settings



def run(plugin):
    while True:
        cmd  = input('请输入指令')
        if cmd == 'exit':
            break
        # 因为无法确定框架使用者是否传入正确的对象所以需要使用反射来检测
        # 判断对象是否具备处理这个指令的方法
        if hasattr(plugin,cmd):
            # 取出对应方法方法
            func = getattr(plugin,cmd)
            # 执行方法处理指令
            func()
        else:
            print('该设备不支持')

    print('see you')


# 创建一个插件对象 调用框架来使用它
wincmd = plugins.WinCMD()
# 框架之外的部分就有自定义对象来完成
# run(wincmd)

# 框架 得根据配置文件拿到需要的类
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)
View Code

元类

元类:创建类的类

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

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

  验证

class Person(object):
    name = 'lanlan'
    pass

p = Person()
print(type(p))

class Student:
    pass
print(type(Student))
View Code

直接调用type类来产生类对象
一个类的三个基本组成部分
1.类的名字(字符类型) 2.类的父类们 (是一个元组或列表) 3.类的名称空间(字典类型)

cls_obj = type('en',(),{})
print(cls_obj)

class Person():
    pass

 

自定义元类

"""
只要继承了type 那么这个类就变成了一个元类

"""

# 定义元类
class Mytype(type):
    def __init__(self,cls_name,bases,dict):
        super().__init__(cls_name,bases,dict)
        print(cls_name,bases,dict)
        if not cls_name.istitle():
            raise Exception('类名错误')


# 为pig类 指定了元类为 MyType
class Pig(metaclass=Mytype):
    pass


class Duck(metaclass=Mytype):
    pass


Mytype('Pig',(),{})
View Code

学习元类的目的

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

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

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

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

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

  

元类中__call__方法

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

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



使用场景:

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

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

 例子:

 

class Mate(type):
    def __init__(self,name,bases,dict):
        super().__init__(name,bases,dict)
        print('init  ')

    def __call__(self, *args, **kwargs):
        print('call')
        print(self)
        print(args)
        print(kwargs)
        return super().__call__(*args,**kwargs)


class Dog(metaclass=Mate):
    def __init__(self,name):
        self.name = name

    def __call__(self, *args, **kwargs):
        print('call')

d = Dog('二狗子')
print(d.name)


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

 

案例:

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

 变成大写
class Mate(type):
    def __call__(self, *args, **kwargs):
        new_args = []
        for i in args:
            new_args.append(i.upper())


        print(new_args)
        print(kwargs)
        return super().__call__(*new_args,**kwargs)

class Person(metaclass=Mate):
    def __init__(self,name,pwd):
        self.name =name
        self.pwd = pwd

p = Person('nana','qwer')
print(p)
print(p.name)
print(p.pwd)
View Code

例子

要求创建对象时必须以关键字参数形式来传参
覆盖元类的__call__
判断你有没有传非关键字参数 == 不能有位置参数
有就炸
class Mate(type):

    def __call__(self, *args, **kwargs):
        if args:
            raise Exception('不允许有位置参数')

        return super().__call__(*args,**kwargs)

    # 
    # def __call__(self, *args, **kwargs):
    #     # 创建一个空对象
    #     obj = object.__new__(self)
    #     # 让对象去初始化
    #     self.__init__(obj,*args,**kwargs)
    # 
    #     return obj


        





class A(metaclass=Mate):
    def __init__(self,name):
        self.name = name

# a = A('lanlan')

a = A(name = 'lanlan')
print(a.name)
View Code

__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)
View Code
__init__方法和__new__方法 都可以实现控制类的创建过程,__init__方法更简单

单例模式

设计模式:用于解决某种固定问题的套路
例如:MVC  MTV等
单例:指的是一个类产生一个对象
为什么要使用单例:单例是为了节省 资源,当一个类的所有对象属性全部相同时,则没有必要创建多个对象

  

class Person():
    def __init__(self,name,age):
        self.name = name
        self.age =age
    def say_hi(self):
        print('hello %s '%self.name)

    #获取对象的方法
    @staticmethod
    def get_instance():
         # 判断是否已经有了对象
        if hasattr(Person,'obj'):
            return getattr(Person,'obj')
        obj = Person('wewe',15)
        Person.obj = obj
        print('新的')
        return obj

p = Person.get_instance()
p.say_hi()

p2 = Person.get_instance()
p2.say_hi()

p3 = Person.get_instance()
p3.say_hi()
普通模式
class Single(type):
    def __call__(self, *args, **kwargs):
#         判断是否有已经存在的对象
        if hasattr(self,'obj'):
            # 取值
            return getattr(self,'obj')
        # 没有就创建
        obj = super().__call__(*args,**kwargs)
        print('创建了')
        # 存入类中
        self.obj = obj
        return obj

class Student(metaclass=Single):
    def __init__(self,name):
        self.name = name


class Person(metaclass=Single):
    pass


# 只会创建一个对象
Person()
Person()
Person()
单例元类

   

 

原文地址:https://www.cnblogs.com/komorebi/p/11272984.html