反射 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('wx',18,'man')
1 hasattr(对象,属性)判断某个对象是否存在某个属性,记得是字符串
if hasattr(p,'name')
2 getattr(对象,属性)对象中取出属性的值
print( getattr(p,'age'))#从对象中取出属性的值,第三个值是默认值,当属性不存在返回的默认值
3 setattr(对象,属性,属性的值)为对象添加新的属性
setattr(p,'id','123')
print(p.id)
4 delattr(对象,要删除的属性)从对象中删除属性
delattr(p,'id')
print(p.id)
查看对象的类信息(字典):
print(p.__dict__)
{'name': 'wx', 'age': 19, 'gender': 'man'}
这些方法本质上就是对__dict__的封装
使用场景:
反射其实就是对属性的增删改查,但是如果直接使用内置的对象.__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)
动态导入
在框架设计中 我们不可能提前知道 框架的用户要提供类相关的信息
需要用到一个模块
import importlib#导入一个导入库,用来导模块的
importlib.import_module()#里面需要放入完整路径
但路径不能是绝对路径。换个电脑出问题。以执行文件作为起始
比如libs文件夹下的plugins
importlib.import_module('libs.plugins')#lib是个包,plugins是模块
返回一个模块。想当于import abc,
import abc
abc='xxx'
在当前名称空间产生一个abc指向一个模块
同理,找个变量接收一下
import importlib
p1 = importlib.import_module('libs.plugins')
print(p1)
只需说明类在何处,叫什么名字!
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)
元类 metaclass
万物皆对象,类当然也是对象
元类是什么,用于创建类的类
对象是通过类实例化产生的,如果类也是对象的话,必然类对象也是有另一个类实例化产生的
默认情况下所有类的元类都是type
class Person:
pass
p = Person()
print(type(p))
print(type(Person))
Person类是通过type类实例化产生的
既然,Person类是通过type类实例化产生的 ,那我们调type能产生类对象吗?
首先:一个类的基本组成是:
class Person:
name='wx'
1.类名(字符类型)2.基类(不写默认为object)(是一个元组或列表)3名称空间(字典类型)
cls_obj = type('dog',(),{})
print(cls_obj)
结果:创建成功
<class '__main__.dog'>
但是type类创建类对象没有必要,直接创建就好了。
type真实目的是:高度定义一个类,例如控制类名
学习元类的目的:
高度的自定义一个类,例如控制类的名字必须以大驼峰的方式来书写
类也是对象,也有自己的类,
我们的需求是创建类对象做一些限制
由于在创建对象干点什么事,想到了初始化(init)方法 我们只要找到类对象的类(元类),覆盖其中 init方法就能实现需求
当然我们不能修改源代码,所以应该继承type来编写自己的元类,同时覆盖init来完成需求
代码:
"""
只要继承了type 那么这个类就变成了一个元类
"""
# 定义了一个元类
class MyType(type):#只要继承了type 那么这个类就变成了一个元类,这样才有元类的那些方法
def __init__(self,clss_name,bases,dict):
super().__init__(clss_name,bases,dict)#一定要调super,因为子类有init方法了
print(clss_name,bases,dict)
if not clss_name.istitle():#限制条件在这里
raise Exception('你类名不会写吗')#抛出异常
MyType('pig',(),{})#生成类,p为小写,报错
# 为pig类指定了元类为MyType
class Pig(metaclass=MyType):#指定元类,P大写,不报错
pass
class Duck(metaclass=MyType):#指定元类
pass
metaclass=type指定元类
此时:
MyType('pig',(),{})#由于MyType继承type元类,现在他也可以创造类了.实例化必然会调init方法
元类中call方法(调用对象立即执行)
注:init方法的执行是由创建对象触发的,即:对象 = 类名() ;而对于 call 方法的执行是由对象后加括号触发的,即:对象() 或者 类()()
class Foo:
def __init__(self):
pass
def __call__(self, *args, **kwargs):
print('__call__')
obj = Foo() # 执行 __init__
obj() # 执行__call__
当一个类通过metaclass方式指定元类时,无需任何参数,直接执行元类中的init内容
class MyMeta(type):
def __init__(self,name,bases,dict):
super().__init__(name,bases,dict)
print("init run")
def __call__(self, *args, **kwargs):
print("元类 call run")
# print(self)
# print(args)
# print(kwargs)
return super().__call__(*args,**kwargs)
class Dog(metaclass=MyMeta): # === Dog = MyMate("Dog",(),{})
def __init__(self,name):
self.name = name
def __call__(self, *args, **kwargs):
print("call run")
# # d = Dog("大黄")
# d = Dog("大黄")
# print(d.name)
使用场景:
当你想要控制对象的创建过程时,就覆盖call方法
当你想要控制类的创建过程时,就覆盖init方法
案例:
实现将对象的所有属性名称转为大写
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)
注意:一旦覆盖了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 __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()