目录
插件化开发... 1
动态导入:... 1
插件化编程技术:... 3
__slots__. 4
未实现和未实现异常:... 6
运算符重载中的反向方法:... 6
例:notepad++中插件;firefox、chrome插件;eclipse;
运行时,根据用户(程序员)需求(如提供字符串),找到模块的资源动态加载起来;
1、__import__(),内建函数;
2、importlib.import_module(),import_module(name, package=None),支持绝对导入和相对导入,相对导入则必须要设置package;
import语句本质上就是调用__import__()这个函数,但不建议直接使用__import__(),建议使用importlib.import_module();
1、内建函数__import__();
__import __(name,globals=None,locals=None,fromlist=(),level=0)
name,模块名;
sys = __import__('sys') #等价于import sys,运行时加载
例:
example_module_test1.py
class A:
def show(self):
print(type(self).__name__)
print(type(self.__module__))
example_plugins.py
if __name__ == '__main__':
mod = __import__('example_module_test1') #同import example_module_test1
getattr(mod,'A')().show()
输出:
A
<class 'str'>
例:
def plugin_load():
mod = __import__('example_module_test1') #加载后会放到sys.modules里,搜索顺序是在sys.path中找
# print(type(mod)) #<class 'module'>
getattr(mod,'A')().show() #getattr(object, name[, default]) -> value,等价于mod.A().show()
# mod.A().show()
if __name__ == '__main__':
plugin_load()
输出:
A
<class 'str'>
例:
def plugin_load(plugin_name:str,sep=':'):
m,_,c = plugin_name.partition(sep)
mod = __import__(m)
cls = getattr(mod,c)
return cls()
if __name__ == '__main__':
# plugin_load()
plugin_load('example_module_test1:A').show()
2、importlib.import_module():
例:
import importlib
def plugin_load(plugin_name:str,sep=':'):
# m,_,c = plugin_name.partition(sep)
m,c = plugin_name.split(sep)
mod = importlib.import_module(m) #推荐用此种,不要用__import__()
cls = getattr(mod,c)
return cls()
if __name__ == '__main__':
# plugin_load()
plugin_load('example_module_test1:A').show()
输出:
A
<class 'str'>
依赖的技术:
reflection,反射,运行时获取类型的信息,可动态维护类型数据;
动态import,推荐使用importlib.import_module(),实现动态import模块的能力;
多线程,可开启一个线程,等待用户输入,从而加载指定名称的模块;
加载的时机:
程序启动时?还是程序运行中?
程序启动时,像pycharm这样的工具,需要很多组件,这些组件也可能是插件,启动的时候扫描固定的目录,加载插件;
程序运行时,程序运行过程中,接受用户指令或请求,启动相应的插件;
两种方式各有利弊,如果插件过多,会导致程序启动很慢,如果用户需要时加载,若插件太多或依赖多,插件也会启动慢;
所以先加载必须的、常用的插件,其它插件使用时,发现需要,动态载入;
应用:
软件的设计不可能尽善尽美,或在某些功能上,不可能做的专业,需要专业的客户自己增强;
如notepadd++,它只需要做好一个文本编辑器就可以了,其它增强功能都通过插件的方式提供,如拼写检查、HTML预览、正则插件等;要定义规范、定义插件从哪里加载、如何加载、必须实现的功能等;
接口和插件区别:
接口往往是暴露出来的功能,接口指的是操作(方法|函数),如模块提供了函数和方法,加载模块后调用这些函数完成功能;接口也是一种规范,它约定了必须实现的功能(必须提供某名称的函数),但不关心怎么实现这个功能;api,application program interface;url指向的是后台应用中某个类的方法;
插件是把模块加载到系统中,运行它,增强当前系统功能,或提供系统不具备的功能,往往插件技术应用在框架设计中,系统本身设计简单化、轻量级、实现基本功能后,其它功能通过插件加入进来,方便扩展;
销售:
插件化需求,旗舰版、家庭版;
另一些软件把相应功能的菜单隐藏了,通过序列号可打开隐藏的这些功能;
软件达到一定规模,必须做成框架,越需要插件化思想;常用的先加载,不常用的懒加载;
都是字典惹的祸,字典为了提升查询效率,必须用空间换时间(为了hash得占用一定的空间);
一般来说,一个对象,属性都存储在字典中便于查询,问题不大;但如果数百万个对象,字典就有点大了;这个时候,能否把属性字典__dict__给省了;py提供了__slots__;
可理解为就给这几个槽位放东西,用__slots__规定有什么样的属性;
实例用;标准库中用得多;
限制实例暴露出的属性供别人使用;
类属性不影响;
应用场景:
未来可能产生大量实例,这些实例中有不需要的属性,用__slots__暴露出可用的属性,且用元组形式列出(是可迭代对象均可,一个属性时字符串也可);
当要使用数百万个对象,且内存容量较为紧张的场景;
__slots__ = 'p1'或__slots__ = 'p1','p2'均可,建议用元组形式__slots__ = ('p1','p2'),__slots__告诉解释器,实例的属性都叫什么,一般来说既然要节约内存,最好还是用元组,一旦类提供了__slots__就阻止实例产生__dict__来保存实例的属性;
继承类的实例不受__slots__影响,__slots__管不到自己的子类,控制不了子类;__slots__不影响子类实例,不会继承下去,除非子类里面自己也定义了__slots__;__slots__一般在子类上用,而且是最下面的子类,父类功能不全;
例:
class A:
x = 123
__slots__ = ('p1','p2') #__slots__ = 'p1'或__slots__ = 'p1','p2'均可,建议用元组形式__slots__ = ('p1','p2'),__slots__告诉解释器,实例的属性都叫什么,一般来说既然要节约内存,最好还是用元组,一旦类提供了__slots__就阻止实例产生__dict__来保存实例的属性
def __init__(self):
self.p1 = 1
self.p2 = 2
def showme(self):
print('I am A.{}'.format(self.p1))
print(A.__dict__)
# print(A().__dict__) #X,实例属性被限制,实例的__dict__消失了
print(A().__slots__)
a = A()
a.p2 = 200
# a.x = 300 # AttributeError: 'A' object attribute 'x' is read-only
A.x = 500
输出:
{'__module__': '__main__', 'x': 123, '__slots__': ('p1', 'p2'), '__init__': <function A.__init__ at 0x7fa9ba0be0d0>, 'showme': <function A.showme at 0x7fa9ba0be158>, 'p1': <member 'p1' of 'A' objects>, 'p2': <member 'p2' of 'A' objects>, '__doc__': None}
('p1', 'p2')
1 200
例:
class A:
x = 123
__slots__ = ('p1','p2')
def __init__(self):
self.p1 = 1
self.p2 = 2
def showme(self):
print('I am A.{}'.format(self.p1))
class B(A):
def __init__(self):
self.b1 = 500
print(B().__dict__) #继承类的实例不受__slots__影响,__slots__管不到自己的子类,控制不了子类;__slots__不影响子类实例,不会继承下去,除非子类里面自己也定义了__slots__;__slots__一般在子类上用,而且是最下面的子类,父类功能不全
输出:
{'b1': 500}
print(type(NotImplemented))
print(type(NotImplementedError))
输出:
<class 'NotImplementedType'> #是个值,单值,是NotImplementedType类的实例
<class 'type'> #是类型,是异常,返回type,父类RuntimeError-->父类Exception
例:
class A:
def showme(self):
raise NotImplementedError
print(A().showme())
例:
class Add:
def __init__(self,x:int):
self.x = x
def __add__(self, other):
print('__add__',self)
# return self.x + other.x
return self.x + other
# def __add__(self, other):
# print('__add__',self)
# try:
# res = self.x + other.x
# except:
# try:
# o = int(other)
# except:
# o = 0
# res = self.x + o
# return res
# def __add__(self, other):
# print('__add__',self)
# try:
# o = int(other.x)
# except:
# o = 0
# return self.x + 0
def __iadd__(self, other):
print('__iadd__',self)
return self.x + other.x
def __radd__(self, other):
print('__radd__',self)
# return self.x + other.x
return self + other
a = Add(4)
b = Add('abc')
# print(a+b)
# print(a+=b)
# print(b+a)
# print(a+1) #不是1(int)没有实现__add__(),int中有所有的方法
print(1+a) #1+a等价于1.__add__(a),而Int类实现了__add__(),不过这个方法对于这种加法的返回值是NotImplemented,解释器发现是这个值,就会发起对第二操作对象的__radd__()调用
输出:
__radd__ <__main__.Add object at 0x7f42d83acfd0>
__add__ <__main__.Add object at 0x7f42d83acfd0>
5
例:
class Add:
def __init__(self,x:int):
self.x = x
def __add__(self, other):
print('__add__',self)
return self.x + other.x
def __iadd__(self, other):
print('__iadd__',self)
return self.x + other.x
def __radd__(self, other):
print('__radd__',self)
# return self.x + other.x
return self + other
class B:
def __init__(self,x):
self.x = x
a = Add(4)
b = B(6)
print(a+b)
print(b+a) #b+a等价于b.__add__(a),但类B没有实现__add__(),就去找a的__radd__()方法
输出:
__add__ <__main__.Add object at 0x7f02a03f7160>
10
__radd__ <__main__.Add object at 0x7f02a03f7160>
__add__ <__main__.Add object at 0x7f02a03f7160>
10
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。