英文:
Exec fails when applied to a code with a new type
问题
I'm your Chinese translator, and here is the translated content you requested:
我有一个名为 multiply.py
的文件,其内容如下:
from typing import NamedTuple
class Result(NamedTuple):
product: int
desc: str
def multiply(a: int, b: int) -> Result:
return Result(
product=a * b,
desc=f"将 {a} 和 {b} 相乘",
)
x = 4
y = 5
print(multiply(x, y))
如果我像这样运行它,当然会得到预期的结果:
$ python multiply.py
Result(product=20, desc='将 4 和 5 相乘')
然而,我试图从 main.py
中使用 exec
函数运行它:
from pathlib import Path
gl, lo = {}, {}
exec(Path("multiply.py").read_text(), gl, lo)
这次输出令人失望:
$ python main.py
Traceback (most recent call last):
File "main.py", line 4, in <module>
exec(Path("multiply.py").read_text(), gl, lo)
File "<string>", line 16, in <module>
File "<string>", line 9, in multiply
NameError: name 'Result' is not defined
为什么会这样?我不能在由 exec
执行的代码中创建新类型吗?
英文:
I have a file multiply.py
with the following contents:
from typing import NamedTuple
class Result(NamedTuple):
product: int
desc: str
def multiply(a: int, b: int) -> Result:
return Result(
product=a * b,
desc=f"muliplied {a} and {b}",
)
x=4
y=5
print(multiply(x, y))
If I run it just like that it of course yields the expected result:
$ python multiply.py
Result(product=20, desc='muliplied 4 and 5')
However I'm trying to run it with exec
function from main.py
:
from pathlib import Path
gl, lo = {}, {}
exec(Path("multiply.py").read_text(), gl, lo)
and this time the output is disappointing:
$ python main.py
Traceback (most recent call last):
File "main.py", line 4, in <module>
exec(Path("multiply.py").read_text(), gl, lo)
File "<string>", line 16, in <module>
File "<string>", line 9, in multiply
NameError: name 'Result' is not defined
Why is that? Can't I create new types in the code executed by exec
?
答案1
得分: 5
由于你为局部变量和全局变量传递了两个不同的字典,根据文档的描述:
> 如果exec
接收到两个分开的对象作为全局和局部变量,代码将会被执行,就好像它嵌套在一个类定义中一样。
类体不是一个封闭的作用域,类体中定义的函数无法访问类体中定义的变量。这就是在方法定义中你必须使用self.
来引用另一个方法的原因。
为了更详细地说明我所说的关于self
的意思,这里有一个实际运行类体中你提供的代码并观察其行为的示例:
class NS:
from typing import NamedTuple
class Result(NamedTuple):
product: int
desc: str
def multiply(a: int, b: int) -> Result:
return Result(
product=a * b,
desc=f"multiplied {a} and {b}",
)
NS.multiply(1, 2)
无论如何,exec
通常被错误使用。有时候它是可以接受的,但是通常情况下,当人们使用exec
/eval
时,Python中通常有更好的方法来做同样的事情。
英文:
Since you are passing two different dictionaries for locals and globals, from the docs:
> If exec
gets two separate objects as globals and locals, the code will
> be executed as if it were embedded in a class definition
Class bodies are not an enclosing scope, functions defined in the class body do not have access to variables defined in the class body. This is the reason that in a method definition you must use self.
to refer to another method.
To elaborate a little on what I mean about self
, here's an example of actually just running the code you have in a class body, and seeing how it behaves:
In [1]: class NS:
...: from typing import NamedTuple
...:
...: class Result(NamedTuple):
...: product: int
...: desc: str
...:
...: def multiply(a: int, b: int) -> Result:
...: return Result(
...: product=a * b,
...: desc=f"muliplied {a} and {b}",
...: )
...:
...:
In [2]: NS.multiply(1, 2)
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
Cell In [2], line 1
----> 1 NS.multiply(1, 2)
Cell In [1], line 9, in NS.multiply(a, b)
8 def multiply(a: int, b: int) -> Result:
----> 9 return Result(
10 product=a * b,
11 desc=f"muliplied {a} and {b}",
12 )
NameError: name 'Result' is not defined
In any case, exec
is a function that is typically misused. There are times when it is perfectly fine, but often it is the case that when people reach for exec
/eval
there is a better way to do it in Python.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论