反射元类

一、反射

  1、什么是反射:就是反省,自省的意思

  反射指的是一个对象应该具备,可以增、删、改、查属性的能力,通过字符串来操作属性

涉及的四个函数,这四个函数就是普通的内置函数,只是没有下划线而已,实现的功能和原理基本一致

hasattr(object,name)  # 判断对象是否实现某个属性
setattr(object,name,value)  # 为对象增加新的属性
getattr(object,name,default) # 从对象中获取某个属性
delattr(object,)  # 从对象中删除某个属性
# 反射
class Person:
    def __init__(self,name,age,gender):
        self.name=name
        self.age=age
        self.gender=gender
p=Person("James",20,"woman")
# print(p.name)
print(hasattr(p,"name"))  # 判断是否是类的属性
if hasattr(p,"name"):
    print(getattr(p,"name",None)) #从对象中取出属性,第三个值位默认值
    # 当属性不存在是返回默认值

# 为对象添加新的属性
setattr(p,"id","1234")
print(p,id)

# 从对象中删除对象
delattr(p,"id")
print(p,id)

>>
True
James
<__main__.Person object at 0x0000018DC46D5E10> <built-in function id>

2、为什么需要反射?

  一个类在定义的时候,可能一些属性的设计并不是很完美,而后期需要作出修改过或删除操作属性时,使用反射可以不需要修改源代码

3、反射的优势:可插拔设计(重点

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

另一个点就是调用另一方提供的对象时,必须先判断这个对象是否满足需求,也就是判断是否是我们需要的属性和方法。

  动态添加模块功能

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

    如此一来,框架就与代码实现了彻底的耦合,只的剩下配置文件

  其实就是当调用其他外界导入的类的功能和方法的时候,是否使用符合当前需求的操作时,用hasattr做一个判断,再用getattr获取属性的方法

二、元类:metaclass

   元类是创建类的类  所有的对象都是实例化或者说调用类而得到的(调用类的过程称为类的实例化)

class创建一个类应该细分为四个过程:   

  1.获取类名

  2.获取基类

  3.获取名称空间

  4.实例化元类得到类

总结:元类即 用于产生类的类

2、自定义元类控制类的创建: 

  通过type高度定义一个类,例如要求所有的方法名称必须小写,类名称必须大写开头等

class MyMate(type):#关键字type
    def __init__(self,name,bases,dic):
        print("run")
        if not dic.get("__doc__"):
            raise TypeError("类必须有文档注释!")
        if not name.istitle():
            raise TypeError("类名必须大写开头!")
        super().__init__(name,bases,dic)
class Foo(object,metaclass=MyMate):  #绑定元类
    pass

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

验证:

class Person:
    pass
p=Person()
print(type(p))
print(type(Person))

>>
<class '__main__.Person'>
<class 'type'>   # Person的类型是type

 4、学习元类的目的:高度的自定义一个类,例如控制类的名字必须以大驼峰体的方式来书写、

 

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

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

# 定义一个元类
class MyType(type):
    def __init__(self,clss_name,bases,dict): #继承后用super调用
        super().__init__(clss_name,bases,dict)
        print(clss_name,bases,dict)
        if not clss_name.istitle():
          raise Exception("类名写错了~")
class pig (metaclass=MyType):
    print("绑定了元类~")
class Duck(metaclass=MyType):
    print("规定的协议要遵守~")

MyType("pig",(),{})

 5、元类中调用__call__方法

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

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

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

实现将对象的所有属性名称转化为大写
class MyType(type):
    def __call__(self, *args, **kwargs):
        new_arg = []   # 要求的书写规范
        for a in args:
            new_arg.append(a.upper()) # 转换为大写

        print(new_arg)
        print(kwargs)
        return super().__call__(*new_arg,**kwargs)
class Person(metaclass=MyType):
    def __init__(self,name,gender):
        self.name=name
        self.gender=gender

p=Person(name="jack",gender="man")
print(p.gender)
print(p.name)

>>

[]
{'name': 'jack', 'gender': 'woman'}
jack
woman

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

 6、__new__的方法:

  当你创建类的对象时,会先执行元类中的__new__ 方法

  执行了__new__函数,就不会再执行__init__,因为__new__函数是真正用于创建类的方法,只有创建类成功了才会执行__init__函数,__new__必须要有返回值且返回值类型为__type__时才会执行__init__函数,

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)

 

7、元类实现单例(单例设计模式)

  什么是单例?

   单例是指的是单个实例,指一个类只能有一个实例对象

  为什么要使用单例?

   为了节省资源,当两个对象的数据完全相同时 则没有必要占用两份资源。

# 单例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()

冒泡算法:

总结规律
圈数  是元素个数减一
次数  元素个数 - 1 - (圈数索引) 


我们需要两层循环 
一层控制圈数 
一层控制次数 

"""
ls = [2,1,3,5,100,24,12,12,1,2,1,1,4,32]
for i in range(len(ls)-1):
    for j in range(len(ls)-1-i):
        # 如果前面的小于后面的则交换位置
        if ls[j] > ls[j+1]:
            ls[j],ls[j+1] = ls[j+1],ls[j]
print(ls)

  

原文地址:https://www.cnblogs.com/Gaimo/p/11272011.html