一.反射
1.什么是反射:程序可以访问,检测和修改本身状态或者行为的一种能力.反射就是通过字符串操作属性
2.涉及的函数:
1.hasattr:判断一个对象中有没有某个属性
2.getattr:从对象中取出属性
3.setattr:修改对象中的属性
4.delattr:删除对象中的属性
class F: def __init__(self, name, age): self.name = name self.age = age f = F('zhang', 17) print(hasattr(f, 'age')) print(getattr(f, 'name')) setattr(f, 'id', 100) print(getattr(f, 'id')) delattr(f, 'id') print(getattr(f, 'id'))
ps: hasattr(object,name) getattr(object, name, default=None) # 默认返回None setattr(x, y, v) delattr(x, y)
3.使用场景
反射其实就是对属性的增删改查,但是如果直接使用内置的__dict__来操作,语法繁琐,不好理解,另一种就是如果对象不是自己写的而是别人提供的,我们就必须判断这个对象是否满足需求,也就是这之中是否有我需求的属性和方法
4.框架设计
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)
二.元类
1.什么是元类:创建类的类,默认情况下所有类的元类是type
class Person: pass p = Person() print(type(p)) print(type(Person)) #运行结果是 <class '__main__.Person'> <class 'type'>
2.用type产生的类基本组成部分:
1.类的名字
2.类的父类们(用一个元组表示)
3.类的名称空间(用字典表示)
cls_obj = type("dog",(),{}) print(cls_obj) # <class '__main__.dog'>
3.学习使用元类的目的
目的:可以高度地自定义类,例如控制一个类的名字必须以大驼峰的方式来书写
类也是对象,也有自己的类
我们的需求是 创建类对象做一些限制
1.想到了初始化方法,我们只要找到了类对象的类(元类),覆盖其中__init__
方法就能实现需求
2.当然我们不能修改源代码,所以应该继承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'>
4.__call__
# 当你调用类对象时,会自动执行元类中的__call__方法,并将这个类作为第一个参数传入,以及后面的一堆参数 # 覆盖元类中的call之后,这个类就无法产生对象对象无法实例化,必须调用super().__call__()来完成对象的创建并返回其返回值
1.想要控制对象的创建的过程__call__
2.想要控制类的创建过程__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>
ps:一旦覆盖了__call__
必须调用父类的__call__
方法来产生对象并返回这个对象
5.__new__方法
当你要创建类对象时(类 + ()
),会首先执行元类中的__new__
方法,拿到一个空对象,然后会自动调用__init__
方法来对这个类进行初始化操作
class MyMeta(type): # def __call__(self, *args, **kwargs): # obj = object.__new__(self) # self.__init__(obj, *args, **kwargs) # return obj pass
ps:如果你覆盖了__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()
四.项目生命周期
1.需求分析 明确用户需求,用户到底需要什么样的程序,要实现什么样的功能,很多时候,用户都是在意淫,逻辑上是不正确的,所以需要工程师,与用户当面沟通以确定用户的真实需求,以及需求的可实现性,并根据最终的需求,产生项目需求分析书 2.计数选型 确定开发该项目采用什么语言、框架、数据库以及版本等等 我们需要根据公司的实际情况考虑采用的框架技术,通常要做的业务业界用主流的实现方案,例如各种框架的版本,要考虑兼容性,流行程度,以及工程师的熟练程度 3.设计项目架构 例如;数据库设计,项目构架(MVC、MTV、三层架构等)的设计 由于项目不可能一次开发完就完事了,后期需要维护扩展,所以良好的架构设计对后续的维护扩展有重要意义 另外如果你的思路从一开始就不正确,那后期很有可能把整个项目推翻重写 项目的设计当然是越早越好,但是学习阶段,直接按照某种架构来编写,你会觉得非常抽象,为什么要这么设计,好处是什么?会造成一种感觉是,还没开始写代码就已经懵逼了 所以要先明确不进行设计前存在的问题,然后在找相应的解决方案 4.开发阶段 项目经理会分配任务给每个人,作为后台开发需要提供接口文档,才能使双方按照相同的协议来进行开发,协作开发需要用到的一些工具:git、svn 项目开发其实是耗时相对较短的一个阶段,前提是需求已经明确,项目设计没有问题,然后根据需求分析书进行代码编写,公司一般都有多个工程师,项目经理负责分配任务,每个工程师开发完自己的模块后提交自己的代码,最后所有代码合并到一起,项目开发完成 5.项目测试 大公司通常会有专门的测试工程师,开发完成并不意味这,工作结束了,需要将完整的项目交个测试工程师,一些小公司可能没有这个岗位,那就需要开发工程师自己完成测试 6.上线部署 需要部署服务器,安装相应的环境,发布代码,还需要为服务器申请一个域名 在测试通过后项目就可以上线运行了,上线运行指的是是你的程序能够真正的运行在互联网上,在开发项目阶段一般使用的都是局域网环境,普通用户是无法访问的,需要将项目部署到服务器上,再为服务器获取一个公网ip,并给这个ip绑定域名,至此项目即正常运行了 7.维护更新 后续都需要增加新功能或是修改各种bug,不断地完善项目,进行版本的更新迭代,当然如果公司不幸倒闭了,那么这个项目的生命周期也就结束了