✍️7.30 反射 元类 生命周期

mac2022-06-30  67

反射 reflect


什么是反射:其实是反省,自省的意思(框架的基石)

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

框架要通过反射 去找到类 找到对象 检测对象是不是想要的

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

涉及的四个函数

hasattr getattr setattr delattr

这些和带双下的魔法函数不一样,这几个都是普通的内置函数,与print...没有区别

class Person: def __init__(self,name,age,gender): self.name = name self.age = age self.gender = gender p = Person("bitten",18,"man") print(hasattr(p,'name')) # 判断某个对象中是否存在某个属性 if hasattr(p,'name'): print(getattr(p,'name',None)) # 从对象中取出值,第三个位置默认值,不存在时返回默认值 setattr(p,'id',123) # setattr # 为对象添加、修改新的属性 print(p.id) # delattr(p,"id") # 为对象删除属性 # print(p.id)

使用场景:

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

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

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

简易框架

执行文件

myframework.py

""" 需求:要实现一个用于处理用户的终端指令的小框架 框架就是已经实现了最基础的构架,就是所有项目都一样的部分 """ from libs 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)

可选用的插件

libs/plugins.py

class WinCMDHandler: def cd(self): print('wincmd 切换目录') def delete(self): print('wincmd 要不要删库') def dir(self): print('wincmd 列出目录') class LinuxCMDHandler: def cd(self): print('linuxcmd 切换目录') def rm(self): print('linuxcmd 要不要删库') def list(self): print('linuxcmd 列出目录')

动态导入

​ 因为在开发过程中,我们往往不能提前知道对方的类在什么地方以及类叫什么,所以我们应该为框架的使用者提供一个配置文件,要求对方将类的信息写入配置文件,然后框架自己去加载需要的模块。

​ 使用动态导入,不需要知道类,只需要告诉路径,然后自己去找。

''' 在框架设计中 我们不可能提前知道 框架的用户要提供类相关的信息 ''' import importlib import abc # 拿到模块 根据模块的路径 pl = importlib.import_module("libs.plugins") print(pl) # 从模块中取出类 cls = getattr(pl,"WinCMD") print(cls) # 实例化产生对象 obj = cls() obj.cd() ######当你的框架中类相关路径保存在settings中时###### ################myframework.py############# from conf import settings import importlib # 1.动态导入模块 # 框架已经实现的部分 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!") # 框架 得根据配置文件拿到需要的类 path = settings.CLASS_PATH # 从配置中单独拿出来 模块路径和 类名称 module_path, class_name = path.rsplit(".", 1) # 2.拿到并导入模块 mk = importlib.import_module(module_path) # 拿到类 cls = getattr(mk,class_name) # 用类实例化出对象 obj = cls() # 调用框架 run(obj) ############conf/settings.py中#################### # 作为框架使用者 在配置文件中指定你配合框架的类是哪个 CLASS_PATH = 'lib.plugin.WinCMD' # 这样就实现了插件的热插拔,需要更换时,把WinCMD换成LinuxCMD就行

元类 metaclass


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

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

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

class Person: pass p = Person() print(type(p), type(Person), type(object)) # <class '__main__.Person'> <class 'type'> <class 'type'> # Person类是通过type类实例化产生的(object类内部是由C语言实现的) # 直接调用type类来产生类对象(默认情况下所有类的元类都是type) class Student: pass print(type(Student)) # <class 'type'>

一个类的基本组成

类的名字(字符类型)类的父类们 (是一个元组或列表)类的名称空间(字典) cls_obj = type("ClassDog", (), {}) print(cls_obj) # <class '__main__.ClassDog'> class Dog: pass print(Dog) # <class '__main__.Dog'>

学习元类目的

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

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

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

想到了初始化方法,我们只要找到了类对象的类(元类),覆盖其中__init__方法就能实现需求当然我们不能修改源代码,所以应该继承type来编写自己的元类,同时覆盖__init__来完成需求 ''' 只要继承了type, 那么这个类就变成了一个元类 ''' class MyType(type): # 定义一个元类 def __init__(self, cls_name, bases, dict): print(cls_name, bases, dict) # Pig () {'__module__': '__main__', '__qualname__': 'Pig'} if not cls_name.istitle(): raise Exception("好好写类名!") super().__init__(cls_name, bases, dict) pass # class pig(metaclass=MyType): # 为Pig类指定了元类为MyType # 直接报错,Exception: 好好写类名! class Pig(metaclass=MyType): pass # # MyType("pig", (), {}) # Exception: 好好写类名! print(MyType("Pig", (), {})) # <class '__main__.Pig'> __call__

元类中的call方法

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

__call__ 与 __init__的使用场景:

想要控制对象的创建过程用__call__想要控制类的创建过程用__init__ ''' 需求:想要把对象的所有属性变成大写 ''' class MyType(type): def __call__(self, *args, **kwargs): new_args = [item.upper() for item in args] return super().__call__(*new_args, **kwargs) class Person(metaclass=MyType): # ---> Person = MyType("Person", (), {}) def __init__(self, name): self.name = name p = Person('jack') print(p.name) # JACK # 要求创建对象时必须以关键字传参 # 覆盖元类的__call__ # 判断有没有传非关键字参数,有就不行 class MyMeta(type): # def __call__(self, *args, **kwargs): # obj = object.__new__(self) # self.__init__(obj, *args, **kwargs) # return obj def __call__(self, *args, **kwargs): if args: raise Exception("非法传参(只能以关键字的形式传参!)") return super().__call__(*args, **kwargs) class MyClass(metaclass=MyMeta): def __init__(self, name): self.name = name # my_class_obj = MyClass('123') # # 报错Exception: 非法传参(只能以关键字的形式传参!) my_class_obj = MyClass(name='123') print(my_class_obj) # <__main__.MyClass object at 0x000002161DD187B8>

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

补充__new__方法

当你要创建类对象时(类 + ()),会首先执行元类中的__new__方法,拿到一个空对象,然后会自动调用__init__方法来对这个类进行初始化操作

class MyMeta(type): # def __call__(self, *args, **kwargs): # obj = object.__new__(self) # self.__init__(obj, *args, **kwargs) # return obj pass

注意:如果你覆盖了__new__方法,则必须保证__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) # <class '__main__.Meta'> # ('A', (), {'__module__': '__main__', '__qualname__': 'A'}) # {} # __new__ run # __init__ run # <class '__main__.A'>

总结:__new__和__init__都可以实现控制类的创建过程,还是__init__更简单

单例设计模式

''' 设计模式? 用于解决某种固定问题的套路 如:MVC、MTV....... 单例:指的是一个类只能产生一个对象,可以节省空间 为什么要单例: 是为了节省空间,节省资源 当一个类的所有对象属性全部相同时则没有必要创建多个对象 ''' 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() # 只会执行一次 # new 了 Person() Person() Person() Person()

项目生命周期


需求分析,需要和甲方沟通确定客户到底需要什么样的功能,还需要进行需求审核,既考虑该需求通过技术手段是否能够实现,最后会产出一个需求文档技术选型,这个阶段主要是确定开发项目使用什么语言,什么框架,什么版本等项目设计,例如数据库的设计,项目构架MVC MTV 三层结构的设计开发阶段,项目经理分配任务给每个人,作为后台开发需要提供接口文档,才能使双方按照相同的协议来进行开发,协作开发需要使用一些工具,git,svn项目测试,大公司通常会有专门的测试工程师。上线部署,需要部署服务器,安装相应的环境,发布代码,还需要为服务器申请域名。更新维护,后续都需要增加新功能或是修改各种bug,不断地完善项目,进行版本的更新迭代,直到这个项目下线,项目的生命周期才算结束

转载于:https://www.cnblogs.com/PowerTips/p/11268169.html

相关资源:SAP GUI for Windows 7.30 Security Guide
最新回复(0)