动态导入
运行时, 根据用户需求(提供字符串), 找到模块的资源进行动态的加载
# 内建函数 __import__
__import__(name, globals=None, locals=None, fromlist=(), level=0)
# name 模块名
# import 语句本质上就是调用这个函数, 但是不鼓励直接使用它, 建议使用下面的模块
import importlib
importlib.import_module()
sys = __import__('sys') # 等价 import sys
# 下面就是插件化编程的核心部分
import importlib
def load_module(string: str, sep='.'):
mod, attr = string.rsplit(sep)
mod = importlib.import_module(mod)
return getattr(mod, attr)
if __name__ == '__main__':
timestamp = load_module('time.time')
print(timestamp())
插件化编程技术
依赖的技术
- 反射: 运行时获取类型的信息, 可以动态维护类型数据
- 动态
import
: 推荐使用importlib
模块, 实现动态import
模块的能力 - 多线程: 可以开启一个线程,等待用户输入,从而加载指定名称的模块
加载的时机
什么时候加载比较合适呢?
程序启动的时候, 还是程序运行中?
- 程序启动时
像pycharm
这样的工具, 需要很多组件, 这些组件也可能时插件, 启动的时候扫描固定的目录, 加载插件.
- 程序运行中
程序运行过程中, 接受用户指令或请求, 启动相应的插件.
两种加载方式各有利弊, 如果插件多, 会导致程序启动很慢, 如果用户需要时再加载, 如果插件太大或者依赖多, 插件也会启动慢.
所以先加载必须的, 常用的插件. 其他插件使用时, 发现需要再动态的载入.
__slots__
问题引出
字典为了提升查询效率, 必须用空间换时间,.一般来说一个对象,属性多一点, 都存储在字典中便于查询, 问题不大.但是如果数百万个对象,那么字典占的内存就有点大.
这个时候, 能不能把属性的字典__dict__
省掉? 于是Python
提供了__slots__
# 插槽
# 添加了 __slots__ 属性, 就限制了实例的属性,且没有__dict__字段
class A:
X = 123
__slots__ = ('p1', 'p2')
def __init__(self):
self.p1 = 12
self.p2 = 14
self.m = 1 # 当定义了__slots__, 这条语句会报错, 因为m没有包含在__slots__中
instance = A()
instance.test = 12 # 这里会抛出异常, 因为test没有在__slots__中, 无法设置属性
print(instance.__dict__) # 这里也会抛出异常, 因为有了__slots__就不会产生dict
print(instance.__slots__) # slots可以节省内存
instance.X = 200 # 这里也不可以进行修改, 因为实例的属性被__slots__控制了
A.X = 200 # 这里可以进行修改, 因为__slots__只是控制了实例, 不会影响到类
__slots__
不会影响到子类, 如果子类中需要__slots__
, 需要自己定义
未实现和未实现异常
NotImplemented
是一个单值None
, 是NotImplementedType
类的实例; NotImplementedError
是一个异常
print(type(NotImplementedError))
print(type(NotImplemented))
# <class 'type'>
# # <class 'NotImplementedType'>
运算符重载中的反向方法
class Add:
def __init__(self, x):
self.x = x
def __add__(self, other):
print('add')
return self.__class__(self.x + int(other))
def __iadd__(self, other):
print('iadd')
self.x += int(other)
return self
def __radd__(self, other):
"""左边+右边, 左边没有实现add方法, 就调用右边的radd"""
print('radd')
return self + other
def __str__(self):
return str(self.x)
def __int__(self):
return self.x
__repr__ = __str__
class B:
def __init__(self, x):
self.x = x
def __int__(self):
return self.x
if __name__ == '__main__':
a = Add(8)
print(a + 1)
print(a + Add(1))
a += Add(1)
b = B(12)
print(b + a) # b没有实现add方法, 于是就调用a的radd方法
# b + a 等价于 b.__add__(a) , 但是 b没有实现add方法, 于是就调用a的radd方法
# 1 + a 等价于 1.__add__(a) , 但是 int类型对于这种加法返回的是一个NotImplemented, 解释器发现是NotImplemented,就会调用右边的__radd__方法