反射 reflect

反射 reflect

什么是反射,其实就是反省,自省的意思

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

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

涉及到的四个函数,这四个函数就是普通的内置函数,没有双下下划线,与print等等没有区别

hasattr getattr setattr delattr

p = Person("jack",18,"man")

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

# 3.为对象增加新的属性
setattr(p,"id","123")
print(p.id)
# 4.从对象中删除属性
delattr(p."id")
print(p.id)

使用场景:

反射其实就是对属性的增删改查,但是如果直接使用内置的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 = plugin.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)

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

元类 metaclass

元类是什么,用于创建类的类

万物皆都对象,类当然也是对象

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

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

验证:

class Person:
    pass
p = Person()

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

Person类是通过type类实例化产生的

学习元类的目的:

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

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

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

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

代码:

"""
只要继承了type 那么这个类就变成了一个元类
"""
# 定义了一个元类
class MyType(type):
    def __init__(self,class_name,bases,dict):
        super().__init__(calss_name,bases,dict)
        print(class_name,bases,dict)
        if not class_name.istitle():
            rsise Exception("你丫的 类名都不会写...")
            
# 为pig类指定了元类为MyType
class Pig(metaclass=MyType):
    pass

class Duck(metaclass=MyType):
    pass

元类中call方法

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

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

使用场景:

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

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

案例:

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

class MyType(type):
    def __call__(self,*args,**kwargs):
        new_args = []
        for a in args:
            new_args.append(a.upper())
        
        print(new_args)
        print(kwargs)
        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)

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

补充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更简单

单例设计模式

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

元类的实现:

# 单例n元类
class Single(type):
    def __init__(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):
    def __init__(self,name):
        self.name = name 
        
class Person(metaclass=Single):
    pass

# 只会创建一个对象
Person()
Person()

冒泡算法

"""
冒泡排序
从小到大
第一圈:
[2,1,3,5]
第一次 得出2的位置
[2,1,3,5]
第二次
[2,3,1,5]
第三次
[2,3,5,1]

次数为 元素个数 -1 -(圈数索引为0)

第二圈:
[2,3,5,1]
第一次
[3,2,5,1]
第二次
[3,5,2,1]
第三次
[3,5,2,1]

次数为 元素个数 -1 -(圈数索引为1)

第三圈:
[2,3,5,1]
第一次
[3,2,5,1]
第二次
[3,5,2,1]

次数为: 元素个数 -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)

"""
编写student类,存储用户信息,

要求改类对象可以使用点语法来操作属性,也可以通过中括号的方式操作属性
"""
class Student(dict):

    def __init__(self,name,age,gender,team_name):
        super().__init__()
        self.name = name
        self.age = age
        self.gender = gender
        self.team_name = team_name


    def __getattr__(self, item):
        return self[item]

    def __setattr__(self, key, value):
        self[key] = value

    def __delattr__(self, item):
        del self[item]

    def __gt__(self, other):
        return self.age > other.age

"""
要求通过班级可以获取到班级下的所有学员 ,思考关联关系,应该如何表示 
""" 
 stu1 = Student("小鸡",2,"woman",c1.name)
# stu2 = Student("小鸭",1,"woman",c1.name)
# stu3 = Student("小鹅",3,"woman","没班级...")

# stus = [stu1,stu2,stu3]

#
# res = []
# for stu in stus:
#     if stu.team_name == c1.name:
#         res.append(stu)
#
#
# print(c1.name,"共有:",res)


# 按照年龄从小到大
stus = [stu1,stu2,stu3]

for i in range(len(stus)):
    for j in range(i,len(stus)-1):
        s1 = stus[j]
        s2 = stus[j+1]
        if s1 > s2:
            stus[j] = s2
            stus[j+1] = s1
            print("交换位置了!")
print(stus)

原文地址:https://www.cnblogs.com/zuihoudebieli/p/11285280.html