给定一个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()
当前回答
从python文档中,这里是你想要的函数:
def my_import(name):
components = name.split('.')
mod = __import__(components[0])
for comp in components[1:]:
mod = getattr(mod, comp)
return mod
简单的__import__不能工作的原因是,任何超过包字符串中第一个点的导入都是你正在导入的模块的属性。因此,像这样的东西是行不通的:
__import__('foo.bar.baz.qux')
你必须像这样调用上面的函数:
my_import('foo.bar.baz.qux')
或者在你的例子中:
klass = my_import('my_package.my_module.my_class')
some_object = klass()
编辑:我在这一点上有点不对头。你基本上想要做的是:
from my_package.my_module import my_class
上面的函数只在fromlist为空时才有必要。因此,适当的调用应该是这样的:
mod = __import__('my_package.my_module', fromlist=['my_class'])
klass = getattr(mod, 'my_class')
其他回答
为现有的答案增加了一点复杂性....
根据用例,显式指定你想导入的类/方法的完整路径(例如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(…)中找到,而不必从外部指定完整的路径。
从python文档中,这里是你想要的函数:
def my_import(name):
components = name.split('.')
mod = __import__(components[0])
for comp in components[1:]:
mod = getattr(mod, comp)
return mod
简单的__import__不能工作的原因是,任何超过包字符串中第一个点的导入都是你正在导入的模块的属性。因此,像这样的东西是行不通的:
__import__('foo.bar.baz.qux')
你必须像这样调用上面的函数:
my_import('foo.bar.baz.qux')
或者在你的例子中:
klass = my_import('my_package.my_module.my_class')
some_object = klass()
编辑:我在这一点上有点不对头。你基本上想要做的是:
from my_package.my_module import my_class
上面的函数只在fromlist为空时才有必要。因此,适当的调用应该是这样的:
mod = __import__('my_package.my_module', fromlist=['my_class'])
klass = getattr(mod, 'my_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)."""
PyPI模块自动加载和导入
# PyPI imports
import pkg_resources, subprocess, sys
modules = {'lxml.etree', 'pandas', 'screeninfo'}
required = {m.split('.')[0] for m in modules}
installed = {pkg.key for pkg in pkg_resources.working_set}
missing = required - installed
if missing:
subprocess.check_call([sys.executable, '-m', 'pip', 'install', '--upgrade', 'pip'])
subprocess.check_call([sys.executable, '-m', 'pip', 'install', *missing])
for module in set.union(required, modules):
globals()[module] = __import__(module)
测试:
print(pandas.__version__)
print(lxml.etree.LXML_VERSION)
module = __import__("my_package/my_module")
the_class = getattr(module, "MyClass")
obj = the_class()