【问题标题】:Python error when creating classes dynamically动态创建类时出现 Python 错误
【发布时间】:2021-06-08 10:25:02
【问题描述】:

让我们来一个具有这种文件夹结构的项目:

> eval
  > a
    > __init__.py
    > a.py
  > b
    > __init__.py
    > b.py
  __init__.py
__init__.py
m.py

a文件夹中__init__.py的内容:

from eval.a.a import A

a.py的内容:

class A:
    def __init__(self) -> None:
        pass

    def __repr__(self) -> str:
        return "Class A"

    def __str__(self) -> str:
        return "Class A"

    def exec(self):
        a = eval('A()');
        print(a)

        b = eval('B()');
        print(b)

b文件夹中__init__.py的内容:

from eval.b.b import B

b.py的内容:

class B:
    def __init__(self) -> None:
        pass

    def __repr__(self) -> str:
        return "Class B"

    def __str__(self) -> str:
        return "Class B "

    def exec(self):
        a = eval('A()');
        print(a)

        b = eval('B()');
        print(b)

eval文件夹中__init__.py的内容:

from eval.a.a import A
from eval.b.b import B

__init__.py eval 文件夹外没有任何内容。

m.py的内容:

from eval.a import A
from eval.b import B

a = eval('A()');
print(a)

b = eval('B()');
print(b)

到目前为止,它可以工作,创建我的 A() 和 B() 对象,打印:

Class A
Class B

但如果我将 m.py 更改为:

from eval.a import A
from eval.b import B

a = eval('A()');
print(a)

b = eval('B()');
print(b)

print('Running A.exec() now:')
a.exec()

print('Running B.exec() now:')
b.exec()

然后我会得到:

Class A
Class B 
Running A.exec() now:
Class A
Traceback (most recent call last):
  File "c:\dev\test\python\teste_eval\m.py", line 19, in <module>
    a.exec()
  File "c:\dev\test\python\teste_eval\eval\a\a.py", line 15, in exec
    b = eval('B()');
  File "<string>", line 1, in <module>
NameError: name 'B' is not defined

那么问题是:

  1. 如果在我创建的文件中我已经导入了 A 类和 B 类,为什么我的 A.exec() 不能使用我的 B 类?

  2. 如何解决?什么意味着让我系统中的任何类都可以由任何类创建,即使是在一个单独的模块中(如本例中的“eval”模块)?

  3. 有没有比使用 eval 函数动态创建这些类的最好方法?

感谢您的支持。

【问题讨论】:

  • 仅仅因为名称B 存在于一些 模块中并不意味着它存在于无处不在。 Python 没有全局命名空间,每个模块都有自己的命名空间。
  • 动态是什么意思? eval 在您的代码中没有任何用处,因此很难理解您想要做什么,而您只需删除 eval 并将源代码直接放入您的 .py 文件中,而不是作为传递的字符串到eval
  • 另请注意,您创建的不是,而是这些类的实例
  • “如果在我创建的文件中我已经导入了类 A 和 B,为什么我的类 B 对我的 A.exec() 不可用?”因为这无关紧要,所以名称必须在使用时可用。您似乎假设作用域是动态工作的,但它不是,python 使用词法作用域,而不是动态作用域。
  • 那你就有点麻烦了,因为 Python 在这里的行为与其他语言有些不同。每个模块都需要显式导入它需要访问的名称。没有全局共享的命名空间。

标签: python class dynamic eval creation


【解决方案1】:

我刚刚找到了一种加载所有模块的方法。

这是一个可能的解决方案:

import sys

def get_user_modules() -> list:
    ret = []
    sm = sys.modules
    for k in sm.keys():
        if (k[0] != '_'):
            if (not k.startswith('encodings.')):
                if (not (k in ['sys', 'builtins', 'nt', 'marshal', 'winreg', 'time', 'zipimport', 'codecs', 
                            'encodings', 'abc', 'io', 'stat', 'genericpath', 'ntpath', 'os', 'site', 'os.path'])):
                    ret.append(k)
    return ret

此代码将返回:

['eval.a.a', 'eval.a', 'eval.b.b', 'eval.b', 'eval']

对于示例中使用的系统结构。

我认为最终的解决方案应该基于这个加载的模块列表。

感谢所有尝试提供帮助的人。

更新:

另一种可能是从起点遍历文件系统到最后一个文件夹,如下所示:

def get_all_modules() -> list:
    r = os.path.dirname(sys.argv[0])
    t = len(r)
    l = []
    for root, dirs, files in os.walk(r):
        s = root[t:].replace('\\', '.')
        for f in files:
            if (f.endswith('.py') and (f[0:2] != '__')):
                fn = f[:-3]
                if (s != ''):
                    fn = s[1:] + '.' + fn
                l.append(fn)
    return l

然后,我可以使用:

def import_all_modules() -> str:
    um = get_all_modules()
    s  = ''
    for k in um:
        if (k != __name__):
            s = s + f'from {k} import *\n'
    return s

要获得我需要导入并执行此操作的所有内容:

exec(import_all_modules())

因此,我将动态导入应用程序中定义的所有类。

【讨论】:

  • 现在,最后一个疑问!如何使用此列表强制导入?我尝试使用eval(s),其中 s 是包含例如from eval.a import * 的字符串,但它不起作用。有人知道我如何在运行时创建导入命令吗?谢谢。
猜你喜欢
  • 2023-04-01
  • 2022-11-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-11-20
  • 1970-01-01
  • 2017-03-26
相关资源
最近更新 更多