python面向对象反射-框架原理-动态导入-元类-自定义类-单例模式-项目的生命周期-05
反射 reflect
反射(reflect)其实是反省,自省的意思
反省:指的是一个对象应该具备可以检测、修改、增加自身属性的能力
反射:通过字符串获取对象或者类的属性,进行操作
设计框架时需要通过反射去检测类的属性,去调用他们
反射涉及的四个函数
这四个就是普通的内置函数,没有双下划线,与print等等没有区别
hasattr getattr setattr delattr
class Person:
def __init__(self, name, age, gender):
self.name = name
self.age = age
self.gender = gender
p = Person('jack', 16, 'man')
print(p.name)
# jack
# -----------------------------------------
# hasattr() 判断某个对象中是否存在某个属性
# -----------------------------------------
if hasattr(p, 'age'):
# -----------------------------------------
# getattr() 从对象中取出值
# 不存在时返回第三个参数
# -----------------------------------------
print(getattr(p, 'age', None))
# 16
# -----------------------------------------
# setattr() 为对象添加新的属性、修改属性
# -----------------------------------------
print(setattr(p, 'name', 'jackson'))
# None
print(getattr(p, 'name'))
# jackson
print(setattr(p, 'school', 'oldboy'))
# None
print(getattr(p, 'school'))
# oldboy
# -----------------------------------------
# delattr() 删除对象的属性
# -----------------------------------------
delattr(p, 'school')
# print(getattr(p, 'school')) # 直接报错,AttributeError: 'Person' object has no attribute 'school'
反射的使用场景
反射其实就是对属性的增删改查,但是如果直接使用内置的__dict__
来写,语法繁琐,不好理解;另一个主要问题是,如果对象不是自己写的,而是另一方提供的,我就必须判断这个对象是否满足需求,也就是是否我需要的属性和方法
"""
反射被称为框架的基石,为什么?
因为框架的设计者,不可能提前知道你的对象到底是怎么设计的
所以你提供给框架的对象 必须通过判断验证之后才能正常使用
判断验证就是反射要做的事情,
当然通过__dict__也是可以实现的, 其实这些方法也就是对__dict__的操作进行了封装
"""
简易框架代码
执行文件代码
myframework.py
代码
# 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)
plugins代码
libs/plugins.py
代码
# libs/plugins.py
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 列出所有文件....")
动态导入
上述框架代码中,写死了,固定了哪个类,这是不合理的,因为无法提前知道对方的类在什么地方以及类叫什么,所以我们应该为框架的使用者提供一个配置文件,要求对方将类的信息写入配置文件,然后框架自己去加载需要的模块
动态导入:不需要提前知道类,告诉路径可以自己找
最后的搭框架代码:
myframework.py
代码
# 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)
libs/plugins.py
代码
# libs/plugins.py
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 列出所有文件....")
conf/settings.py
文件
# conf/settings.py
CLASS_PATH = "libs.plugins.WinCMD"
如此一来,框架就与实现代码彻底解耦了,只剩下配置文件了
元类 metaclass
元类是用于创建类的类
在python中,万物皆对象,类当然也是对象
推理:对象是通过类实例化产生的,如果类也是对象的话,必然类对象也是由另一个类实例化产生的
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
项目开发其实是耗时相对较短的一个阶段,前提是需求已经明确,项目设计没有问题,然后根据需求分析书进行代码编写,公司一般都有多个工程师,项目经理负责分配任务,每个工程师开发完自己的模块后提交自己的代码,最后所有代码合并到一起,项目开发完成
项目测试
大公司通常会有专门的测试工程师,开发完成并不意味这,工作结束了,需要将完整的项目交个测试工程师,一些小公司可能没有这个岗位,那就需要开发工程师自己完成测试
上线部署
需要部署服务器,安装相应的环境,发布代码,还需要为服务器申请一个域名
在测试通过后项目就可以上线运行了,上线运行指的是是你的程序能够真正的运行在互联网上,在开发项目阶段一般使用的都是局域网环境,普通用户是无法访问的,需要将项目部署到服务器上,再为服务器获取一个公网ip,并给这个ip绑定域名,至此项目即正常运行了
更新维护
后续都需要增加新功能或是修改各种bug,不断地完善项目,进行版本的更新迭代,当然如果公司不幸倒闭了,那么这个项目的生命周期也就结束了
python面向对象反射-框架原理-动态导入-元类-自定义类-单例模式-项目的生命周期-05的更多相关文章
- 老王Python培训视频教程(价值500元)【基础进阶项目篇 – 完整版】
老王Python培训视频教程(价值500元)[基础进阶项目篇 – 完整版] 教学大纲python基础篇1-25课时1.虚拟机安装ubuntu开发环境,第一个程序:hello python! (配置开发 ...
- Python基础篇【第3篇】: Python异常处理、反射、动态导入、利用反射的web框架
异常处理 什么是异常? 异常即是一个事件,该事件会在程序执行过程中发生,影响了程序的正常执行. 一般情况下,在Python无法正常处理程序时就会发生一个异常.异常是Python对象,表示一个错误.当P ...
- python面向对象(六)之元类
元类 1. 类也是对象 在大多数编程语言中,类就是一组用来描述如何生成一个对象的代码段.在Python中这一点仍然成立: In [13]: class ObjectCreator(object): . ...
- python 反射、动态导入
1. 反射 hasattr(obj,'name') # 判断对象中是否含有字符串形式的方法名或属性名,返回True.False getattr(obj,'name',None) ...
- 封装,封装的原理,Property ,setter ,deleter,多态,内置函数 ,__str__ , __del__,反射,动态导入模块
1,封装 ## 什么是封装 what 对外隐藏内部的属性,以及实现细节,并给外部提供使用的接口 学习封装的目的:就是为了能够限制外界对内部数据的方法 注意 :封装有隐藏的意思,但不是单纯的隐藏 pyt ...
- day26 封装、多态、内置函数、反射、动态导入
今日内容 1.封装 什么是封装? 封装从字面意思上看就只将某种东西封起来装好,当我们代码中的某些方法与属性不想让外界进行访问时,就对这些属性进行特殊的处理,使这种属性或者方法不能被外界直接进行访问或者 ...
- python模块之os_sys_动态导入_包
os模块: #!/usr/bin/env python # coding:utf-8 import os print(os.getcwd()) #获取当前工作目录,即当前python脚本工作的目录路径 ...
- python面向对象 : 反射和内置方法
一. 反射 1. isinstance()和issubclass() isinstance( 对象名, 类名) : 判断对象所属关系,包括父类 (注:type(对象名) is 类名 : 判断对象所属 ...
- python面向对象编程(2)—— 实例属性,类属性,类方法,静态方法
1 实例属性和类属性 类和实例都是名字空间,类是类属性的名字空间,实例则是实例属性的名字空间. 类属性可通过类或实例来访问.只有通过类名访问时,才能修改类属性的值. 例外的一种情况是,当类属性是一个 ...
随机推荐
- BZOJ 4823 Luogu P3756 [CQOI2017]老C的方块 (网络流、最小割)
题目链接 (Luogu) https://www.luogu.org/problem/P3756 (BZOJ) http://lydsy.com/JudgeOnline/problem.php?id= ...
- Spring boot 读取resource目录下的文件
背景:最近做项目重构将以前的ssh + angular js架构,重构为spring boot + vue.项目是一个数据管理平台,后台涉及到多表关联查询,数据导入导出等. 问题:读取resource ...
- 使用IDEA集成Spring框架时右下角警戒
反正看到报错就不爽,就要去解决它 这个警戒的意思大概就是: spring配置检查 找到未映射的Spring配置文件. 请配置Spring的Facet. 那这玩意怎么配置? 点击IDEA右上角的Proj ...
- leetcode124二叉树最大路径和
第一选择是将其转化成图用动态规划,但这样还是太麻烦 使用递归的思路,对于当前的节点root,分别求左右孩子到当前节点的单项路径权值最大的路径权值,然后记包含当前节点的路径权值为 path_price= ...
- windows实用cmd命令总结
D: 进入D盘 cd D:\eclipse 进入D盘后进入D盘下的某个路径 Ipconfig 查看计算机ip Cls 清空命令行 ping ip(主机名) 测试网络是否畅通 Help 查看所有的d ...
- LC 535. Encode and Decode TinyURL
Note: This is a companion problem to the System Design problem: Design TinyURL. TinyURL is a URL sho ...
- 自定义一个数组对象工具demo
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...
- 代码实现:从键盘输入接收一个文件夹路径,打印出该文件夹下所有的.java文件名
package com.loaderman.test; import java.io.File; import java.io.FileReader; import java.util.Scanner ...
- flutter 打包
iOS打包 iOS打包需要注意一下一些设置 info.plist 设置ATS.白名单.字符串等等 Assets.xcassets 替换icon,替换LaunchImage中内容 注意LaunchIma ...
- Redis ==> 集群的三种模式
一.主从同步/复制 通过持久化功能,Redis保证了即使在服务器重启的情况下也不会丢失(或少量丢失)数据,因为持久化会把内存中数据保存到硬盘上,重启会从硬盘上加载数据. 但是由于数据是存储在一台服务器 ...