후크를 사용하여 Python 3의 메모리에서 동적으로 모듈 가져 오기
Nov 25 2020
내가 달성하고 싶은 것은 이 답변이 제안 하는 것과 정확히 일치 하지만 Python 3에서는.
아래 코드는 Python 2에서 잘 작동합니다.
import sys
import imp
modules = {
"my_module":
"""class Test:
def __init__(self):
self.x = 5
def print_number(self):
print self.x"""}
class StringImporter(object):
def __init__(self, modules):
self._modules = dict(modules)
def find_module(self, fullname, path):
if fullname in self._modules.keys():
return self
return None
def load_module(self, fullname):
if not fullname in self._modules.keys():
raise ImportError(fullname)
new_module = imp.new_module(fullname)
exec self._modules[fullname] in new_module.__dict__
return new_module
if __name__ == '__main__':
sys.meta_path.append(StringImporter(modules))
from my_module import Test
my_test = Test()
my_test.print_number() # prints 5
그러나 Python 3 (exec를 포함하고 괄호로 인쇄)을 명백하게 변경하면 다음 코드가 표시됩니다.
import sys
import imp
modules = {
"my_module":
"""class Test:
def __init__(self):
self.x = 5
def print_number(self):
print(self.x)"""}
class StringImporter(object):
def __init__(self, modules):
self._modules = dict(modules)
def find_module(self, fullname, path):
if fullname in self._modules.keys():
return self
return None
def load_module(self, fullname):
if not fullname in self._modules.keys():
raise ImportError(fullname)
new_module = imp.new_module(fullname)
exec(self._modules[fullname])
return new_module
if __name__ == '__main__':
sys.meta_path.append(StringImporter(modules))
from my_module import Test
my_test = Test()
my_test.print_number() # Should print 5
그 exec()
변화가 상당히 중요 하지는 않았습니다 . 나는 그 줄이 파이썬 2에서 무엇을했는지 이해하지 못했습니다. 나는 그것이 옳다고 생각하는 방식으로 "번역"했습니다. 그러나 Python 3 코드는 다음 오류를 제공합니다.
Traceback (most recent call last):
File "main.py", line 35, in <module>
from my_module import Test
File "<frozen importlib._bootstrap>", line 991, in _find_and_load
File "<frozen importlib._bootstrap>", line 975, in _find_and_load_unlocked
File "<frozen importlib._bootstrap>", line 655, in _load_unlocked
File "<frozen importlib._bootstrap>", line 626, in _load_backward_compatible
KeyError: 'my_module'
Python 2에서 작동하는 것과 똑같은 방식으로 Python 3에서 작동하려면 코드에서 무엇을 변경해야합니까?
관찰 : 에서 모듈을 가져 오는 데 관심 이 없기 때문에 이것은 내 질문에 대답하지 않습니다 .pyc
.
답변
1 ZachGates Nov 27 2020 at 08:28
짧은 대답은 exec
코드 샘플에서 문장 의 후반부를 번역하는 것을 잊었다는 것입니다 . 이로 인해이 메서드 의 컨텍스트 exec
가 적용됩니다 . 따라서 컨텍스트를 지정하십시오.in
load_module
new_module
exec(self._modules[fullname], new_module.__dict__)
그러나 Python 버전 3.4 이상을 사용하면 PEP 451 ( 모듈 사양 도입)과 모듈 의 사용 중단 imp이 적용 importlib됩니다. 특별히:
- 이 imp.new_module(name)기능은로 대체됩니다
importlib.util.module_from_spec(spec)
. - 메타 경로 찾기 객체에 대한 추상 기본 클래스가 제공
importlib.abc.MetaPathFinder
됩니다.. - 그리고 이러한 파인더 객체는 현재 사용
find_spec
보다는find_module
.
다음은 코드 샘플을 매우 가깝게 재 구현 한 것입니다.
import importlib
import sys
import types
class StringLoader(importlib.abc.Loader):
def __init__(self, modules):
self._modules = modules
def has_module(self, fullname):
return (fullname in self._modules)
def create_module(self, spec):
if self.has_module(spec.name):
module = types.ModuleType(spec.name)
exec(self._modules[spec.name], module.__dict__)
return module
def exec_module(self, module):
pass
class StringFinder(importlib.abc.MetaPathFinder):
def __init__(self, loader):
self._loader = loader
def find_spec(self, fullname, path, target=None):
if self._loader.has_module(fullname):
return importlib.machinery.ModuleSpec(fullname, self._loader)
if __name__ == '__main__':
modules = {
'my_module': """
BAZ = 42
class Foo:
def __init__(self, *args: str):
self.args = args
def bar(self):
return ', '.join(self.args)
"""}
finder = StringFinder(StringLoader(modules))
sys.meta_path.append(finder)
import my_module
foo = my_module.Foo('Hello', 'World!')
print(foo.bar())
print(my_module.BAZ)