通过import模块,我们可以引用其他文件中定义的方法,变量.
test.py
def f1():
print('f1')
def f2():
print('f2')
def f3():
print('f3')
def f4():
print('f4')
a = 1
import test as ss
ss.f1()
ss.f2()
print(ss.a)
那么是否可以通过模块名动态导入,python提供了__import__来实现这一功能,可以随时访问到导入模块中的方法或者变量。
imp = input(“请输入你想导入的模块名:”)
CC = __import__(imp) 這种方式就是通过输入字符串导入你所想导入的模块
CC.f1() # 执行模块中的f1方法
上面还存在一点问题:那就是我的模块名有可能不是在本级目录中存放着。有可能是如下图存放方式:
那么这种方式我们该如何搞定呢?看下面代码:
dd = __import__("lib.text.commons") #这样仅仅导入了lib模块 dd = __import__("lib.text.commons",fromlist = True) #改用这种方式就能导入成功
但是官方不推荐这种方法,主要是面对解释器的,对于动态导入模块,官方推荐通过 importlib来实现
import importlib
#__import__('import_lib.metaclass') #这是解释器自己内部用的
importlib.import_module('import_lib.metaclass') #与上面这句效果一样,官方建议用这个
通过模块名动态导入模块,从而使我们能够执行该模块中的函数。那么是否可以直接通过函数名称来执行函数呢?
#dynamic.py
imp = input("请输入模块:")
dd = __import__(imp) # 等价于import imp
inp_func = input("请输入要执行的函数名称:")
f = getattr(dd,inp_func,None)#作用:从导入模块中找到需要调用的函数inp_func,然后返回一个该函数的引用.没有找到就返回None
f() # 执行该函数
通过字符串映射或修改程序运行时的状态、属性、方法, 有以下4个方法
class Foo(object):
def __init__(self):
self.name = 'wupeiqi'
def func(self):
return 'func'
obj = Foo()
# #### 检查是否含有成员 ####
hasattr(obj, 'name')
hasattr(obj, 'func')
#判断对象obj是否包含名为name的属性,及func的方法(hasattr是通过调用getattr(ojbect, name)是否抛出异常来实现的)
# #### 获取成员 ####
print(getattr(obj, 'name'))#如果Instance 对象中有属性name则打印self.name的值,否则打印'None'
getattr(obj, 'func','default')#如果有方法method,返回其地址,否则返回default
print (getattr(obj, 'func', 'default')()) #如果有方法method,运行函数并打印None否则打印default
# #### 设置成员 ####
setattr(obj, 'age', 18)
setattr(obj, 'show', lambda num: num + 1)
# #### 删除成员 ####
delattr(obj, 'name')
delattr(obj, 'func')
反射代码示例
注:getattr,hasattr,setattr,delattr对模块的修改都在内存中进行,并不会影响文件中真实内容。
基于反射机制模拟web框架路由
需求:比如我们输入:www.xxx.com/commons/f1,返回f1的结果。
# 动态导入模块,并执行其中函数
url = input("url: ")
target_module, target_func = url.split('/')
m = __import__('lib.'+target_module, fromlist=True)
#inp = url.split("/")[-1] # 分割url,并取出url最后一个字符串
if hasattr(m,target_func): # 判断在commons模块中是否存在inp这个字符串
target_func = getattr(m,target_func) # 获取inp的引用
target_func() # 执行
else:
print("404")
首先,我们并没有定义任何一行import语句;
其次,用户的输入url被要求为类似“commons/home”这种格式,其实也就是模拟web框架里的url地址,斜杠左边指向模块名,右边指向模块中的成员名。
然后,modules,func = url.split("/")处理了用户输入,使我们获得的2个字符串,并分别保存在modules和func变量里。
接下来,最关键的是m = __import__(modules)这一行,它让程序去导入了modules这个变量保存的字符串同名的模块,并将它赋值给m变量。
最后的调用中,getattr去modules模块中调用func成员的含义和以前是一样的。
总结:通过__import__函数,我们实现了基于字符串的动态的模块导入。
同样的,这里也有个小瑕疵!
如果我们的目录结构是这样的:
那么在模块调用语句中,必须进行修改,我们想当然地会这么做:
1 2 3 4 5 6 7 8 9 10 11 12 |
|
改了这么一个地方:obj = __import__("lib." + modules),看起来似乎没什么问题,和import lib.commons的传统方法类似,但实际上运行的时候会有错误。
1 2 3 4 5 |
|
为什么呢?因为对于lib.xxx.xxx.xxx这一类的模块导入路径,__import__默认只会导入最开头的圆点左边的目录,也就是“lib”。我们可以做个测试,在同级目录内新建一个文件,代码如下:
1 2 |
|
执行结果:
1 |
|
这个问题怎么解决呢?加上fromlist = True参数即可!
1 2 3 4 5 6 7 8 9 10 11 12 |
|
至此,动态导入模块的问题基本都解决了,只剩下最后一个,那就是万一用户输入错误的模块名呢?比如用户输入了somemodules/find,由于实际上不存在somemodules这个模块,必然会报错!那有没有类似上面hasattr内置函数这么个功能呢?答案是没有!碰到这种,你只能通过异常处理来解决。
单例模式,是一种常用的软件设计模式。在应用这个模式时,单例对象的类必须保证只有一个实例存在。许多时候整个系统只需要拥有一个的全局对象,这样有利于我们协调系统整体的行为。
用装饰器方式实现
def wapper(cls):
instances = {}
def inner():
if cls not in instances:
instances[cls] = cls()
return cls
return inner
@wapper
def Foo():
pass
f1 =Foo()
f2 =Foo()
print(f1 is f2)
静态方法实现:
class ConnectPool:
__instatnce=None
@staticmethod
def get_instance():
if ConnectPool.__instatnce:
return ConnectPool.__instatnce
else:
ConnectPool.__instatnce = ConnectPool()
return ConnectPool.__instatnce
obj =ConnectPool.get_instance()
print(obj)
obj1 =ConnectPool.get_instance()
print(obj1)