反射与元类
# 反射机制
# Python是一个开源的强类型的动态语言
- 反射是被视为动态语言的关键
- 动态语言
- 在给变量指定数据的时候,不需要指定数据类型
- 静态语言
- 所有变量都是一开始就定义好的,比如类型等,需要用到时则直接拿来使用。
# 反射介绍
- 反射是一种能力
- 指的是在程序运行过程中,可以"动态"地获取对象地信息(数据属性、函数属性)
- 动态指的是事先不知道,只在最后要用到时才会去获取其属性
- 动态获取程序信息以及动态调用对象的功能称为反射机制
- 对于任意一个类,都可以知道这个类的所有属性和方法
- 对于任意一个对象,都能够调用他的任意方法和属性
# 反射作用
- 可以通过字符串去操作对象的属性,或者判断一个对象有没有某个属性
- 反射可以十分灵活地操作对象的属性,比如将用户交互的结果反射到具体的功能执行
- 如用户输入get命令,则去调用get方法
- 输入pull命令,则去调用pull方法
# 实现反射机制的步骤
- 不仅是对象,类和函数也可以
- 通过4个函数实现(建议)
hasattr(obj, ‘字符串属性名’, 默认返回值)
- 判断一个对象是否有某个属性
- 默认返回值会在没有这个属性时返回
getattr(obj, ‘字符串属性名’, 默认返回值)
- 通过字符串来获取对象的属性值
- 默认返回值会在没有这个属性时返回
setattr(obj, ‘字符串属性名’, 要设置的值)
- 通过字符串设置对象的属性的值
delattr(obj, ‘字符串属性名’)
- 通过字符串删除对象的属性
- 直接通过dict实现(不建议)
- 先通过
dir()
查看一个对象下,有哪些属性
dir(obj)
可以查看一个对象下有哪些属性- 就是显示.能显示的属性
- 先通过
- 通过字符串反射到真正的属性上,得到属性值
obj.__dict__['字符串属性名']
- 但是有局限性
# 类的内置方法
# 内置方法介绍
- 定义在类的内部,以
__
开头,并以__
结尾的方法 - 特点是会在某种情况下自动触发执行
# 内置方法的作用
- 可以定制化我们的类或对象,以在某些情况下,自动执行某些代码
# 常用内置方法的使用
__str__()
打印对象时会触发- 类中定义__str__后 print(实例化的对象) 时会执行__str__方法下的代码,并将其返回值打印
class App:
def __str__(self):
print('你在打印我!')
return '必须返回字符串类型'
a = App()
print(a)
1
2
3
4
5
6
2
3
4
5
6
__del__()
在删除对象时会触发- 包括程序结束时的清理回收也会触发,会在被删除前执行
- 如果类中打开调用了系统资源,可以在
__del__()
中执行系统资源的回收操作- 防止程序资源回收了,打开的系统资源还占着
# 元类
# 元类介绍
- class关键字创建的类,都是基于元类而来的,也就是类的类就是元类
- 元类 —-实例化-—> 类 —-实例化-—> 对象
# 元类作用
- 自定义元类,对类进行控制
- 比如规定类的属性名或类名必须大写等
# 查看元类
print(type(类名))
# class创造类机制分析
- 拿到类名
- 拿到类的父类(基类)
- 执行类体代码拿到的类的名称空间字典
- 通过exec函数
- 将以上作为参数调用元类
type()
- type(类名, 父类, 名称空间字典)
- 会得到一个类 (和用class关键字创建的一样)
- 将以上作为参数调用元类
# 自定义元类
- 创建自定义元类
- 只有继承了type类的类才是元类
class 元类名(type):
def __init__(self, a, b, c):
pass # 可在此处写控制代码
1
2
3
2
3
- 调用自定义元类
- metaclass是指定元类,默认是type
- 自定义时可以修改为自己的元类
- 同等于
类名 = type(类名, 父类, 名称空间字典)
class 类名(metaclass=type):
pass
1
2
2
- 调用元类过程
- 先通过元类中的
__new__()
方法造一个空对象
- 会自动传入当前所在类,和调用类时传入的参数
- 先通过元类中的
- 调用元类中的
__init__()
方法,完成初始化的操作
- 会自动传入
__new__()
造好的对象 - 可在此处进行类的控制
- 调用元类中的
- 返回初始化好的对象
- 以上1和2都是通过type.call()方法调用的
- 初始化好的对象也是通过type.call()返回的
def __new__(cls, *args, **kwargs):
# 必须返回一个对象
# 可以问父类要,或者调用type.__new__()
# return type.__new__(cls, *args, **kwargs)
return super().__new__(cls, *args, **kwargs)
1
2
3
4
5
2
3
4
5
# 元类下的属性查找
- 对象 —> 类 —> 父类 —> object类
- 不会找到元类
# exec函数
exec('字符串代码', {}, namespace_dict)
- 会将字符串代码执行产生的名称,作为字典,丢到namespace_dict变量里
# call()方法
# 介绍
- 定义的是
"名称()"
时执行的操作 - 如果想让一个对象可以加括号调用,需要在该对象的类中添加
__call__()
方法
# 注意
- 元类的
__call__()
是定制不了的 - 自定义元类中定义的
__call__
实际是控制生成的类的调用操作的 - 正常的
__call__()
- 调用
__new__()
并传入参数获得对象
- 调用
- 将调用
__new__()
返回的对象作为参数,传入__init__()
- 将调用
- 例如
class MyMeta(type):
def __call__(self, *args, **kwargs):
obj = self.__new__(self)
self.__init__(obj, *args, **kwargs)
return obj
1
2
3
4
5
2
3
4
5
# 例如
class Foo:
def __call__(self, name):
print(name)
obj = Foo()
obj('Nanni')
1
2
3
4
5
2
3
4
5
# 单例模式
# 介绍
- 单例模式就是,一个类最多只会有1个对象,这种设计模式可以节省资源消耗
# 实现方式
- 装饰器方式实现
- 通过装饰器来判断,每次创造实例时,首先查看该类是否存在实例
- 存在的话直接返回该实例即可,否则新建一个实例并存放在字典中,然后再返回
def singleton(cls):
_instance = {}
def wrapper():
if cls not in _instance:
_instance[cls] = cls()
return _instance[cls]
return wrapper
@singleton
class Single:
pass
a = Single()
b = Single()
print(a is b)
1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
- 使用
__new__()
实现- 使用
__new__
方法在创造实例时进行干预,达到实现单例模式的目的 - 在每次创建实例时,查看该类是否被实例化过
- 如果_instance为None,则创建一个实例并将其赋值给_instance,然后再返回它(实例对象)
- 如果_instance不为None则直接返回它(实例对象)
- 使用
class Single():
_instance = None
def __new__(cls, *args, **kwargs):
if cls._instance is None:
cls._instance = object.__new__(cls, *args, **kwargs)
return cls._instance
single1 = Single()
single2 = Single()
print(id(single1) == id(single2))
1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
# 三层架构设计(MVC)
# 用户视图层
- 用于与用户进行交互,并且接受用户输入的数据,然后调用对应的接口层函数进行处理
- 例如:登录功能(输入用户名、密码、登录)、注册功能等
# 逻辑接口层
- 逻辑接口层(最核心的层,应当在此层记录日志)
- 相当于用户视图层与数据处理层的桥梁,用于接收用户视图层传来的数据,并进行'核心的逻辑'校验
- 校验过程中,会去访问'数据处理层',并接收其处理结果,然后返回给用户视图层展示
- 如:登录功能,用户名是否存在、如果存在则校验密码等等
# 数据处理层
- 用于专门接受接口层传递过来的请求,并进行响应的处理,增删改查等,并放回结果给逻辑接口层
- 如:查询用户名,有则返回用户数据,没有则返回None