exec(variable =)在for循环中分配的变量范围?

Scope of variables assigned by exec(variable =) in a for loop?

我知道大多数人不喜欢使用 exec 进行动态分配。但是,出于我的目的(务实和古怪),我想将变量名称视为数据。

考虑这个例子,我在 class 初始化期间使用它

现在我要给这些变量动态赋值

 class Example(object):
      __tree_dict = {"self.genomedir": "genomes", 
                "self.pre_align": "pre_alignment", 
                "self.alignments": "alignments",
                "self.trimmed": "trimmed",
                "self.prf_pre": "prf_preprocess",
                "self.scaled": "scaled",
                "self.csv": "csv"}

      def __init__(self, rootdir):
           self._rootdir = Path(rootdir)
           self.outdir = self.mksubdir(self._rootdir, "out")

        for variable, human_readable in self.__tree_dict.items():
            try:
                exec(variable + " = self.mksubdir(self.outdir,human_readable)")
            except:
                 LOGGER.error("%s failed to initialize as %s, defaulting output to root" (variable, human_readable), exc_info=True)
                 exec(variable + '= Path(self._rootdir)')            

这段代码会运行,但我不确定异常处理是否真的有效,因为当我添加一个 finally 语句将变量赋值写入记录器时,例如

        finally:
            LOGGER.info(variable + "set to %s" % getattr(self, variable))

python 解释器提出

  AttributeError: 'Example' object has no attribute 'self.csv'

(属性名称在 运行 时更改,因为字典未排序 - 属性本身并不重要)

重要的问题是,当我在 for 循环范围之外引用新变量时,可以访问它们而没有属性错误。他们的分配已经发生,他们是 class 的属性。这些变量在 dir(self) 和 self 中都可以找到。

这里 python 的什么特性阻止我访问 for 块(或 finally 块)内的这些变量?

编辑:

一个独立的例子:

 class Example(object):
       __vars_to_be_assigned: {"self.foo": "value", "self.bar": "not foo"}

       def __init__(self):
             for key, value in self.__vars_to_be_assigned:
                   try:
                        exec(key + " = value")
                   except:
                        print("Exception!")
                   else:
                        print(getattr(self, variable[5:]))

这个例子应该引发一个 AttributeError

这确实是 exec 的一个错误用法。做起来更简单:

class Example(object):
      __tree_dict = {"genomedir": "genomes", 
                "pre_align": "pre_alignment", 
                "alignments": "alignments",
                "trimmed": "trimmed",
                "prf_pre": "prf_preprocess",
                "scaled": "scaled",
                "csv": "csv"}

      def __init__(self, rootdir):
           self._rootdir = Path(rootdir)
           self.outdir = self.mksubdir(self._rootdir, "out")

        for variable, human_readable in self.__tree_dict.items():
            try:
                setattr(self, variable, self.mksubdir(self.outdir,human_readable))
            except:
                 LOGGER.error("%s failed to initialize as %s, defaulting output to root" (variable, human_readable), exc_info=True)
                 setattr(self, variable, Path(self._rootdir)

您收到错误的原因很简单,就是该属性被称为 csv,而不是 self.csv。使用 setattr 并从 __tree_dict 中的值中删除 self. 前缀将确保设置和获取值之间的一致性。