给定一个Python类的字符串,例如my_package.my_module。MyClass,最好的加载方式是什么?
换句话说,我正在寻找一个等效的Class.forName()在Java,函数在Python。它需要工作在谷歌应用程序引擎。
最好是这样一个函数,它接受类的FQN作为字符串,并返回类的引用:
my_class = load_class('my_package.my_module.MyClass')
my_instance = my_class()
给定一个Python类的字符串,例如my_package.my_module。MyClass,最好的加载方式是什么?
换句话说,我正在寻找一个等效的Class.forName()在Java,函数在Python。它需要工作在谷歌应用程序引擎。
最好是这样一个函数,它接受类的FQN作为字符串,并返回类的引用:
my_class = load_class('my_package.my_module.MyClass')
my_instance = my_class()
当前回答
在谷歌应用引擎中有一个名为import_string的webapp2函数。更多信息请参见:https://webapp-improved.appspot.com/api/webapp2.html
So,
import webapp2
my_class = webapp2.import_string('my_package.my_module.MyClass')
例如,这在webapp2中使用。可以使用处理程序或字符串的路由。
其他回答
为现有的答案增加了一点复杂性....
根据用例,显式指定你想导入的类/方法的完整路径(例如package.subpackage.module…)可能有点不方便。在importlib之上,我们可以利用__init__.py使事情更加简洁。
假设我有一个python包,像这样:
├── modes
│ ├── __init__.py
│ ├── bar.py
│ ├── foo.py
│ ├── modes.py
Foo.py,比如说,有一些类/函数,我们想在程序的其他地方使用:
from modes.modes import Mode
class Foo(Mode):
def __init__(self, *arg, **kwargs):
super(Foo, self).__init__(*arg, **kwargs)
def run(self):
self.LOG.info(f"This is FOO!")
使用命令行参数,我可以传递一个与我想要运行的模式对应的参数。我希望能够像这样
def set_mode(mode):
""" """
import importlib
module = importlib.import_module('modes.foo')
getattr(module, mode)().run()
输出:
>> set_mode("Foo")
>> engine_logger:INFO - This is FOO!
这很好,但是我们真正想要得到的是:
def set_mode(mode):
""" """
import importlib
module = importlib.import_module('modes') # only import the package, not modules explicitely
getattr(module, mode)().run()
这会引发一个错误:
>> set_mode("Foo")
>> AttributeError: module 'modes' has no attribute 'Foo'
但是,我们可以在/modes/__init__.py中添加以下内容:
from .foo import Foo
from .bar import Bar
然后,我们可以做:
>> set_mode("Foo")
>> engine_logger:INFO - This is FOO!
>> set_mode("Bar")
>> engine_logger:INFO - This is BAR!
在其他情况下,我们在init.py中导入的所有子模块/函数/类都可以直接在importlib.import_module(…)中找到,而不必从外部指定完整的路径。
在谷歌应用引擎中有一个名为import_string的webapp2函数。更多信息请参见:https://webapp-improved.appspot.com/api/webapp2.html
So,
import webapp2
my_class = webapp2.import_string('my_package.my_module.MyClass')
例如,这在webapp2中使用。可以使用处理程序或字符串的路由。
def import_class(cl):
d = cl.rfind(".")
classname = cl[d+1:len(cl)]
m = __import__(cl[0:d], globals(), locals(), [classname])
return getattr(m, classname)
module = __import__("my_package/my_module")
the_class = getattr(module, "MyClass")
obj = the_class()
如果你不想滚动你自己的,pydoc模块中有一个函数可以做到这一点:
from pydoc import locate
my_class = locate('my_package.my_module.MyClass')
与这里列出的其他方法相比,这种方法的优点是locate将在提供的虚线路径中找到任何python对象,而不仅仅是模块中的对象。例如my_package.my_module.MyClass.attr。
如果你好奇他们的配方是什么,下面是函数:
def locate(path, forceload=0):
"""Locate an object by name or dotted path, importing as necessary."""
parts = [part for part in split(path, '.') if part]
module, n = None, 0
while n < len(parts):
nextmodule = safeimport(join(parts[:n+1], '.'), forceload)
if nextmodule: module, n = nextmodule, n + 1
else: break
if module:
object = module
else:
object = __builtin__
for part in parts[n:]:
try:
object = getattr(object, part)
except AttributeError:
return None
return object
它依赖于pydoc。safeimport函数。以下是相关文档:
"""Import a module; handle errors; return None if the module isn't found.
If the module *is* found but an exception occurs, it's wrapped in an
ErrorDuringImport exception and reraised. Unlike __import__, if a
package path is specified, the module at the end of the path is returned,
not the package at the beginning. If the optional 'forceload' argument
is 1, we reload the module from disk (unless it's a dynamic extension)."""