反射和元类
反射:
反射也称反省,就是指一个程序应该具备检查自身属性和修改自身属性的功能。
反射式通过字符串来操作属性。
涉及到四个函数,这四个函数就是普通的内置函数,没有双下划线,与print没有什么区别。
四个方法:
1.hastattr 是判断某个对象是否存在某个属性和值
2.getattr 是获取某个对象的属性和值
3.setattr 是为对象添加新的属性
4.delattr 是删除对象中的属性。
使用场景:
反射其实就是对属性的增删改查,但是如果直接使用内置的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)
最后的框架代码:
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)
如此一来框架就与实现代码彻底解耦了,只剩下配置文件
元类:
元类就是创建类的类
在python中万物皆对象,所以类也是对象。
对象是类通过类实例化产生的,如果累也是对象的话,必然类对象也是有另一个类实例化产生的,
默认所有的元类是type
验证:
class Person:
pass
p = Person()
print(type(p))
print(type(Person))
Person类是通过type类实例化产生的
学习目的:
学习元类是为了限制类的创建过程。
元类中初始化有3个参数,一个是类名,一个是类的父类们,一个是类的名称空间。
我们自定义一个元类,覆盖元类中的__init__方法然后再让需要限制的类指定这个元类,完成对类的创建过程。
一个类只要继承了type,那么它就是元类。
# 定义一个元类 class MyType(type): def __init__(self,class_name,bases,dict): super().__init__(class_name,bases,dict) if not class_name.istitle(): raise Exception(22222) class Person(metaclass=MyType): pass
__call__方法:
这普通类中就是对象加括号调用执行也就是,对象加括号。如果是类对象加括号或者实例化产生一个对象时也会执行。
lass 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)
__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)
单例模式:
就是一个类只会产生一个对象,为了节省空间。
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()
冒泡算法:
"" 冒泡排序 从大到小 第一圈: [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] 次数为 元素个数 - 1 - (圈数索引为1) 第三圈: [3,5,2,1] 第一次 [5,3,2,1] 次数为 元素个数 - 1 - (圈数索引为2) 总结规律 圈数 是元素个数减一 次数 元素个数 - 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)
总结规律:就是圈数是:元素个数减1,次数:元素个数减1减圈数索引
项目生命周期
1.需求分析
2.技术选型
3.项目设计
4.项目开发
5.项目测试
6.上线运行
7.维护更新
各阶段的主要内容:
1.明确用户需求,用户到底需要什么样的程序,要实现什么样的功能,很多时候,用户都是在意淫,逻辑上是不正确的,所以需要工程师,与用户当面沟通以确定用户的真实需求,以及需求的可实现性,并根据最终的需求,产生项目需求分析书
2.我们需要根据公司的实际情况考虑采用的框架技术,通常要做的业务业界用主流的实现方案,例如各种框架的版本,要考虑兼容性,流行程度,以及工程师的熟练程度
3.由于项目不可能一次开发完就完事了,后期需要维护扩展,所以良好的架构设计对后续的维护扩展有重要意义
另外如果你的思路从一开始就不正确,那后期很有可能把整个项目推翻重写
项目的设计当然是越早越好,但是学习阶段,直接按照某种架构来编写,你会觉得非常抽象,为什么要这么设计,好处是什么?会造成一种感觉是,还没开始写代码就已经懵逼了 所以要先明确不进行设计前存在的问题,然后在找相应的解决方案
4.项目开发其实是耗时相对较短的一个阶段,前提是需求已经明确,项目设计没有问题,然后根据需求分析书进行代码编写,公司一般都有多个工程师,项目经理负责分配任务,每个工程师开发完自己的模块后提交自己的代码,最后所有代码合并到一起,项目开发完成
5.开发完成并不意味这,工作结束了,需要将完整的项目交个测试工程师,一些小公司可能没有这个岗位,那就需要开发工程师自己完成测试
6.在测试通过后项目就可以上线运行了,上线运行指的是是你的程序能够真正的运行在互联网上,在开发项目阶段一般使用的都是局域网环境,普通用户是无法访问的,需要将项目部署到服务器上,再为服务器获取一个公网ip,并给这个ip绑定域名,至此项目即正常运行了
7.后续都需要增加新功能或是修改各种bug,不断地完善项目,进行版本的更新迭代,当然如果公司不幸倒闭了,那么这个项目的生命周期也就结束了
补充:
动态导入模块(importlib):
就是提前不知道是什么模块,所以在需要的时候以字符串类型的包名称来导入模块
静态导入模块:
import.... from ... import...都是属于静态导入模块就是建立在某个基础下,已经提前知道了这个模块