Exec fails when applied to a code with a new type

huangapple go评论80阅读模式
英文:

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) -&gt; Result:
    return Result(
        product=a * b,
        desc=f&quot;muliplied {a} and {b}&quot;,
    )

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=&#39;muliplied 4 and 5&#39;)

However I'm trying to run it with exec function from main.py:

from pathlib import Path

gl, lo = {}, {}
exec(Path(&quot;multiply.py&quot;).read_text(), gl, lo)

and this time the output is disappointing:

$ python main.py 
Traceback (most recent call last):
  File &quot;main.py&quot;, line 4, in &lt;module&gt;
    exec(Path(&quot;multiply.py&quot;).read_text(), gl, lo)
  File &quot;&lt;string&gt;&quot;, line 16, in &lt;module&gt;
  File &quot;&lt;string&gt;&quot;, line 9, in multiply
NameError: name &#39;Result&#39; 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) -&gt; Result:
   ...:         return Result(
   ...:             product=a * b,
   ...:             desc=f&quot;muliplied {a} and {b}&quot;,
   ...:         )
   ...:
   ...:

In [2]: NS.multiply(1, 2)
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
Cell In [2], line 1
----&gt; 1 NS.multiply(1, 2)

Cell In [1], line 9, in NS.multiply(a, b)
      8 def multiply(a: int, b: int) -&gt; Result:
----&gt; 9     return Result(
     10         product=a * b,
     11         desc=f&quot;muliplied {a} and {b}&quot;,
     12     )

NameError: name &#39;Result&#39; 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.

huangapple
  • 本文由 发表于 2023年5月11日 01:52:11
  • 转载请务必保留本文链接:https://go.coder-hub.com/76221322.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定