进行动态导入,该导入在Python中仍然是显式的
好吧,这有点复杂 假设我在一个包中有一个模块:进行动态导入,该导入在Python中仍然是显式的,python,intellisense,dynamic-import,Python,Intellisense,Dynamic Import,好吧,这有点复杂 假设我在一个包中有一个模块: a_package |-- __init__.py |-- a_module.py 在a_模块.py中我声明a_类: # file location: a_package/a_module.py class A_Class(): def say(self): print ("cheese") 我可以创建一个A_类的实例,并通过以下操作调用say方法: from a_package.a_module import A_
a_package
|-- __init__.py
|-- a_module.py
在a_模块.py中
我声明a_类
:
# file location: a_package/a_module.py
class A_Class():
def say(self):
print ("cheese")
我可以创建一个A_类
的实例,并通过以下操作调用say
方法:
from a_package.a_module import A_Class
my_object = A_Class()
my_object.say() # this will display 'cheese' as expected
但是,我希望采用更动态的方法(我计划有很多包和类,并且希望使代码更易于编写)。因此,我创建了一个名为load\u class
def load_class(package_name, module_name, class_name)
result = None
try:
exec('from ' + package_name + '.' + module_name + ' import ' + class_name)
exec('result = '+class_name)
except:
raise ImportError('Unable to load the class')
return result
# Now, I can conveniently do this:
A_Class = load_class('a_package', 'a_module', 'A_Class')
my_object = A_Class()
my_object.say()
# or even shorter:
load_class('a_package', 'a_module', 'A_Class')().say()
程序按预期工作,但IDE(我使用pydev)不理解我的代码,无法执行intellisense(自动完成代码)
如果我使用第一种方法,intellisense显然是有效的:
from a_package.a_module import A_Class
my_object = A_Class()
my_object. # when I press ".", there will be a popup to let me choose "say" method
但如果我使用第二种方法,intellisense无法为我完成:
load_class('a_package', 'a_module', 'A_Class')(). # when I press ".", nothing happened
我知道,这是在Python中进行动态导入的折衷。但是,我想知道是否有其他方法可以让我使用第二种方法(可能不使用exec
),它仍然可以让通用IDE的intellisense(如Pydev)猜测类内的方法
编辑:我为什么需要这样做?
假设我有这样一个目录结构
fruit
|-- strawberry.py
|-- orange.py
chocolate
|-- cadbury.py
|-- kitkat.py
need_dynamic.py
在need_dynamic.py
中,我有以下脚本:
food_list = ['fruit', 'chocolate']
subfood_list = [['strawberry', 'orange'],['cadbury', 'kitkat']]
# show food list and ask user to choose food
for i in xrange(len(food_list)):
print i + " : " + food_list[i]
food_index = int(raw_input('chose one'))
# show subfood list and ask user to choose subfood
for i in xrange(len(subfood_list[food_index])):
print i + " : " + subfood_list[food_index][i]
subfood_index = int(raw_input('chose one'))
# init the class
my_class = load_class(food_list[food_index], subfood_list[food_index, subfood_index])
# the rest of the code
这只是为了简化,实际上,我计划通过获取目录自动填充食物列表
和子食物列表
假设您有一个数据分类框架,并希望让用户选择他们想要使用的方法。用户还应该能够通过简单地添加python包和模块来扩展框架
我希望这个例子是合理的
再次编辑接受的答案无法解决智能感知问题。但它展示了如何更好地编写代码。我认为这是IDE的问题,而不是python的问题。我将发布另一个问题。您希望使用
\uuu导入\uu
内置:
def load_class(package, mod_name, cls_name):
mod = __import__('.'.join((package, mod_name)))
return getattr(mod, cls_name)
当然,如果您愿意,您可以将错误处理放回那里,老实说,我不确定您首先为什么要这样做。对于大多数事情来说,动态导入似乎是一种“代码味道”。在开始使用之前,请评估您是否确实需要它。好的,这是您的解决方案:
:/tmp/dynamic_import:~ ls
chocolate fruit need_dynamic.py
:/tmp/dynamic_import:~
need_dynamic.py
fruit
|-- strawberry.py
|-- orange.py
chocolate
|-- cadbury.py
|-- kitkat.py
:/tmp/dynamic_import:~ cat orange.py
class Orange(object):
def say(self):
return "Say cheese from class: %s" % __name__
:/tmp/dynamic_import:~
#!/usr/bin/env python
import os
import sys
import inspect
def load_modules_from_path(path):
"""
Import all modules from the given directory
"""
# Check and fix the path
if path[-1:] != '/':
path += '/'
# Get a list of files in the directory, if the directory exists
if not os.path.exists(path):
raise OSError("Directory does not exist: %s" % path)
# Add path to the system path
sys.path.append(path)
# Load all the files in path
for f in os.listdir(path):
# Ignore anything that isn't a .py file
if len(f) > 3 and f[-3:] == '.py':
modname = f[:-3]
# Import the module
__import__(modname, globals(), locals(), ['*'])
def load_class_from_name(fqcn):
# fqcn = fully qualified classname
# Break apart fqcn to get module and classname
paths = fqcn.split('.')
modulename = '.'.join(paths[:-1])
classname = paths[-1]
# Import the module
__import__(modulename, globals(), locals(), ['*'])
# Get the class
cls = getattr(sys.modules[modulename], classname)
# Check cls
if not inspect.isclass(cls):
raise TypeError("%s is not a class" % fqcn)
# Return class
return cls
def main():
food_list = ['fruit', 'chocolate']
subfood_list = [['strawberry', 'orange'],['cadbury', 'kitkat']]
# show food list for users to select.
for i in xrange(len(food_list)):
print '%d: %s' % (i, food_list[i])
food_index = int(raw_input('Choose one: '))
for i in xrange(len(subfood_list[food_index])):
print '%d: %s' % (i, subfood_list[food_index][i])
subfood_index = int(raw_input('Chose one: '))
pkg = food_list[food_index]
module = subfood_list[food_index][subfood_index]
class_name = module.title()
load_modules_from_path(pkg)
new_class = load_class_from_name('%s.%s' % (module, class_name))
# instantiation
obj = new_class()
print obj.say()
if __name__ == '__main__': main()
:/tmp/dynamic_import:~ python need_dynamic.py
0: fruit
1: chocolate
Choose one: 0
0: strawberry
1: orange
Chose one: 0
Say cheese from class: strawberry
:/tmp/dynamic_import:~ python need_dynamic.py
0: fruit
1: chocolate
Choose one: 1
0: cadbury
1: kitkat
Chose one: 0
Say cheese from class: cadbury
:/tmp/dynamic_import:~
要使用名称作为字符串导入模块,请执行以下操作:
从模块路径加载类:
目录结构:
:/tmp/dynamic_import:~ ls
chocolate fruit need_dynamic.py
:/tmp/dynamic_import:~
need_dynamic.py
fruit
|-- strawberry.py
|-- orange.py
chocolate
|-- cadbury.py
|-- kitkat.py
:/tmp/dynamic_import:~ cat orange.py
class Orange(object):
def say(self):
return "Say cheese from class: %s" % __name__
:/tmp/dynamic_import:~
#!/usr/bin/env python
import os
import sys
import inspect
def load_modules_from_path(path):
"""
Import all modules from the given directory
"""
# Check and fix the path
if path[-1:] != '/':
path += '/'
# Get a list of files in the directory, if the directory exists
if not os.path.exists(path):
raise OSError("Directory does not exist: %s" % path)
# Add path to the system path
sys.path.append(path)
# Load all the files in path
for f in os.listdir(path):
# Ignore anything that isn't a .py file
if len(f) > 3 and f[-3:] == '.py':
modname = f[:-3]
# Import the module
__import__(modname, globals(), locals(), ['*'])
def load_class_from_name(fqcn):
# fqcn = fully qualified classname
# Break apart fqcn to get module and classname
paths = fqcn.split('.')
modulename = '.'.join(paths[:-1])
classname = paths[-1]
# Import the module
__import__(modulename, globals(), locals(), ['*'])
# Get the class
cls = getattr(sys.modules[modulename], classname)
# Check cls
if not inspect.isclass(cls):
raise TypeError("%s is not a class" % fqcn)
# Return class
return cls
def main():
food_list = ['fruit', 'chocolate']
subfood_list = [['strawberry', 'orange'],['cadbury', 'kitkat']]
# show food list for users to select.
for i in xrange(len(food_list)):
print '%d: %s' % (i, food_list[i])
food_index = int(raw_input('Choose one: '))
for i in xrange(len(subfood_list[food_index])):
print '%d: %s' % (i, subfood_list[food_index][i])
subfood_index = int(raw_input('Chose one: '))
pkg = food_list[food_index]
module = subfood_list[food_index][subfood_index]
class_name = module.title()
load_modules_from_path(pkg)
new_class = load_class_from_name('%s.%s' % (module, class_name))
# instantiation
obj = new_class()
print obj.say()
if __name__ == '__main__': main()
:/tmp/dynamic_import:~ python need_dynamic.py
0: fruit
1: chocolate
Choose one: 0
0: strawberry
1: orange
Chose one: 0
Say cheese from class: strawberry
:/tmp/dynamic_import:~ python need_dynamic.py
0: fruit
1: chocolate
Choose one: 1
0: cadbury
1: kitkat
Chose one: 0
Say cheese from class: cadbury
:/tmp/dynamic_import:~
这是fruit中的一个模块,我用大写字母为模块名命名类名:
:/tmp/dynamic_import:~ ls
chocolate fruit need_dynamic.py
:/tmp/dynamic_import:~
need_dynamic.py
fruit
|-- strawberry.py
|-- orange.py
chocolate
|-- cadbury.py
|-- kitkat.py
:/tmp/dynamic_import:~ cat orange.py
class Orange(object):
def say(self):
return "Say cheese from class: %s" % __name__
:/tmp/dynamic_import:~
#!/usr/bin/env python
import os
import sys
import inspect
def load_modules_from_path(path):
"""
Import all modules from the given directory
"""
# Check and fix the path
if path[-1:] != '/':
path += '/'
# Get a list of files in the directory, if the directory exists
if not os.path.exists(path):
raise OSError("Directory does not exist: %s" % path)
# Add path to the system path
sys.path.append(path)
# Load all the files in path
for f in os.listdir(path):
# Ignore anything that isn't a .py file
if len(f) > 3 and f[-3:] == '.py':
modname = f[:-3]
# Import the module
__import__(modname, globals(), locals(), ['*'])
def load_class_from_name(fqcn):
# fqcn = fully qualified classname
# Break apart fqcn to get module and classname
paths = fqcn.split('.')
modulename = '.'.join(paths[:-1])
classname = paths[-1]
# Import the module
__import__(modulename, globals(), locals(), ['*'])
# Get the class
cls = getattr(sys.modules[modulename], classname)
# Check cls
if not inspect.isclass(cls):
raise TypeError("%s is not a class" % fqcn)
# Return class
return cls
def main():
food_list = ['fruit', 'chocolate']
subfood_list = [['strawberry', 'orange'],['cadbury', 'kitkat']]
# show food list for users to select.
for i in xrange(len(food_list)):
print '%d: %s' % (i, food_list[i])
food_index = int(raw_input('Choose one: '))
for i in xrange(len(subfood_list[food_index])):
print '%d: %s' % (i, subfood_list[food_index][i])
subfood_index = int(raw_input('Chose one: '))
pkg = food_list[food_index]
module = subfood_list[food_index][subfood_index]
class_name = module.title()
load_modules_from_path(pkg)
new_class = load_class_from_name('%s.%s' % (module, class_name))
# instantiation
obj = new_class()
print obj.say()
if __name__ == '__main__': main()
:/tmp/dynamic_import:~ python need_dynamic.py
0: fruit
1: chocolate
Choose one: 0
0: strawberry
1: orange
Chose one: 0
Say cheese from class: strawberry
:/tmp/dynamic_import:~ python need_dynamic.py
0: fruit
1: chocolate
Choose one: 1
0: cadbury
1: kitkat
Chose one: 0
Say cheese from class: cadbury
:/tmp/dynamic_import:~
这是您的主要脚本:
:/tmp/dynamic_import:~ ls
chocolate fruit need_dynamic.py
:/tmp/dynamic_import:~
need_dynamic.py
fruit
|-- strawberry.py
|-- orange.py
chocolate
|-- cadbury.py
|-- kitkat.py
:/tmp/dynamic_import:~ cat orange.py
class Orange(object):
def say(self):
return "Say cheese from class: %s" % __name__
:/tmp/dynamic_import:~
#!/usr/bin/env python
import os
import sys
import inspect
def load_modules_from_path(path):
"""
Import all modules from the given directory
"""
# Check and fix the path
if path[-1:] != '/':
path += '/'
# Get a list of files in the directory, if the directory exists
if not os.path.exists(path):
raise OSError("Directory does not exist: %s" % path)
# Add path to the system path
sys.path.append(path)
# Load all the files in path
for f in os.listdir(path):
# Ignore anything that isn't a .py file
if len(f) > 3 and f[-3:] == '.py':
modname = f[:-3]
# Import the module
__import__(modname, globals(), locals(), ['*'])
def load_class_from_name(fqcn):
# fqcn = fully qualified classname
# Break apart fqcn to get module and classname
paths = fqcn.split('.')
modulename = '.'.join(paths[:-1])
classname = paths[-1]
# Import the module
__import__(modulename, globals(), locals(), ['*'])
# Get the class
cls = getattr(sys.modules[modulename], classname)
# Check cls
if not inspect.isclass(cls):
raise TypeError("%s is not a class" % fqcn)
# Return class
return cls
def main():
food_list = ['fruit', 'chocolate']
subfood_list = [['strawberry', 'orange'],['cadbury', 'kitkat']]
# show food list for users to select.
for i in xrange(len(food_list)):
print '%d: %s' % (i, food_list[i])
food_index = int(raw_input('Choose one: '))
for i in xrange(len(subfood_list[food_index])):
print '%d: %s' % (i, subfood_list[food_index][i])
subfood_index = int(raw_input('Chose one: '))
pkg = food_list[food_index]
module = subfood_list[food_index][subfood_index]
class_name = module.title()
load_modules_from_path(pkg)
new_class = load_class_from_name('%s.%s' % (module, class_name))
# instantiation
obj = new_class()
print obj.say()
if __name__ == '__main__': main()
:/tmp/dynamic_import:~ python need_dynamic.py
0: fruit
1: chocolate
Choose one: 0
0: strawberry
1: orange
Chose one: 0
Say cheese from class: strawberry
:/tmp/dynamic_import:~ python need_dynamic.py
0: fruit
1: chocolate
Choose one: 1
0: cadbury
1: kitkat
Chose one: 0
Say cheese from class: cadbury
:/tmp/dynamic_import:~
以下是输出:
:/tmp/dynamic_import:~ ls
chocolate fruit need_dynamic.py
:/tmp/dynamic_import:~
need_dynamic.py
fruit
|-- strawberry.py
|-- orange.py
chocolate
|-- cadbury.py
|-- kitkat.py
:/tmp/dynamic_import:~ cat orange.py
class Orange(object):
def say(self):
return "Say cheese from class: %s" % __name__
:/tmp/dynamic_import:~
#!/usr/bin/env python
import os
import sys
import inspect
def load_modules_from_path(path):
"""
Import all modules from the given directory
"""
# Check and fix the path
if path[-1:] != '/':
path += '/'
# Get a list of files in the directory, if the directory exists
if not os.path.exists(path):
raise OSError("Directory does not exist: %s" % path)
# Add path to the system path
sys.path.append(path)
# Load all the files in path
for f in os.listdir(path):
# Ignore anything that isn't a .py file
if len(f) > 3 and f[-3:] == '.py':
modname = f[:-3]
# Import the module
__import__(modname, globals(), locals(), ['*'])
def load_class_from_name(fqcn):
# fqcn = fully qualified classname
# Break apart fqcn to get module and classname
paths = fqcn.split('.')
modulename = '.'.join(paths[:-1])
classname = paths[-1]
# Import the module
__import__(modulename, globals(), locals(), ['*'])
# Get the class
cls = getattr(sys.modules[modulename], classname)
# Check cls
if not inspect.isclass(cls):
raise TypeError("%s is not a class" % fqcn)
# Return class
return cls
def main():
food_list = ['fruit', 'chocolate']
subfood_list = [['strawberry', 'orange'],['cadbury', 'kitkat']]
# show food list for users to select.
for i in xrange(len(food_list)):
print '%d: %s' % (i, food_list[i])
food_index = int(raw_input('Choose one: '))
for i in xrange(len(subfood_list[food_index])):
print '%d: %s' % (i, subfood_list[food_index][i])
subfood_index = int(raw_input('Chose one: '))
pkg = food_list[food_index]
module = subfood_list[food_index][subfood_index]
class_name = module.title()
load_modules_from_path(pkg)
new_class = load_class_from_name('%s.%s' % (module, class_name))
# instantiation
obj = new_class()
print obj.say()
if __name__ == '__main__': main()
:/tmp/dynamic_import:~ python need_dynamic.py
0: fruit
1: chocolate
Choose one: 0
0: strawberry
1: orange
Chose one: 0
Say cheese from class: strawberry
:/tmp/dynamic_import:~ python need_dynamic.py
0: fruit
1: chocolate
Choose one: 1
0: cadbury
1: kitkat
Chose one: 0
Say cheese from class: cadbury
:/tmp/dynamic_import:~
请让我知道这是否有效。您的方法比从package.module import klass执行
更好吗?后者更具语义,不会让其他程序员对您的代码感到困惑。这也让我可以通过使用配置变量调用不同的方法和类。我不知道intellisense是否能嗅出这个。(这似乎不太可能)。但是,如果要动态导入,这是一种方法(使用\uuuu import\uuuu
而不是exec
)。是的,您的代码看起来更干净。我也不确定。我相信这就是为什么web2py不是智能感知友好型的。为了更好的理由,为什么我要这么做,我编辑了我的答案。ThxUnfortuly似乎getattr
只对类有效,而对模块无效,而且inspect.get\u members()
似乎太多了。当您有一堆模块并且不想加载所有内容时,动态导入非常有用。好的,cls=getattr(sys.modules[modulename],classname)
执行这个技巧即使我有一个大的监控脚本,我也会根据硬件类型加载模块和类,所以我不需要每次都将一堆模块加载到主脚本中。