【问题标题】:Python variables scope in loops example循环示例中的 Python 变量范围
【发布时间】:2016-04-09 19:21:41
【问题描述】:

我是 Python 新手,对在循环内声明的变量的范围感到困惑。我看过一些例子,但在我的具体情况下很难理解。

比如我看到了如下代码段here

with ZipFile(self.archive_name, "r") as archive:
    for id, files in data.items():
        if files:
            print("Creating", id)
            dirpath = os.path.join(directory, id)

            os.mkdir(dirpath)

            for file in files:
                match = pattern.match(filename)
                new = match.group(2)
                new_filename = os.path.join(dirpath, new)

                content = archive.open(file).read()
            with open(new_filename, "wb") as outfile:
                outfile.write(content)

我基本上以非常相同的方式重复了上面的代码,但在循环中执行了不同的语句。 这些相似的代码段在我的__main__ 中一个接一个。我的问题是:在那个重复代码中,我是否需要为变量赋予新名称 对于archiveidfilefilesoutfile 是否?会不会有冲突什么的?是否有任何良好实践问题需要牢记?

【问题讨论】:

  • 修改了我的问题以添加有关这些相似代码段所在位置的信息。

标签: python


【解决方案1】:

假设这段代码在一个函数中,那么变量的作用域就是函数的末尾。如果此代码处于模块级别,则变量的范围是模块(也称为全局)范围。

你没有使用不同的名字。以下代码只会在不同的时间将不同的对象分配给archive 变量:

with ZipFile(self.archive_name, "r") as archive:
    print(id(archive))

with ZipFile(self.archive_name, "r") as archive:
    print(id(archive))

相当于这样:

archive = ZipFile(self.archive_name, "r")
with archive:
    print(id(archive))
archive = ZipFile(self.archive_name, "r")
with archive:
    print(id(archive))

也就是说,与with 语句和循环相关的块并没有定义变量的范围,它们是“只是”赋值。您应该看到为不同对象的 id 打印了两个不同的值。

请注意,由于您的示例代码使用id 作为变量名,因此我使用内置函数id 的示例应谨慎使用!

有什么好的做法需要注意吗?

有误入自以为是的风险:

  • 您很少在循环外使用循环变量的值。因此,通常可以稍后在函数的新循环中再次使用相同的循环变量,但一般情况下,您应该检查 all 之前在函数中对该变量名的使用再次使用它,可以肯定。对于模块级代码,情况更糟:在添加第二个循环之前,您需要确保模块的外部用户没有依赖具有第一个循环留在其中的值的变量。
  • 除非该对象在两个不同的地方提供完全相同相同的角色,那么尽管稍后在函数中重用变量是安全的,但它仍然可能有点混乱。
  • 显然,在将代码复制粘贴两次到函数中之前,您希望合理地确定在您的特定情况下,重复(可能会进行一些更改)确实比定义另一个函数并调用它两次要好。

【讨论】:

  • 感谢您的出色回答。只需要澄清一下;您说“如果此代码处于模块级别,则变量的范围是模块(又称全局)范围”,这是我的代码所在的位置。因此,“不必使用不同的名称”。这让我很困惑。我的意思是,由于它是全局范围,id 变量在首次声明时,它不会“保持”它的值直到结束吗?对不起,如果我理解这一点很慢:)
  • @hask.duke: 如果这段代码是模块级的,那么id 变量将保存你给它的值直到文件结束,或者直到你给它分配一个新值它(这是您的第二个 for 循环将执行的操作)。此外,导入您的模块的其他模块将能够以your_module_name.id 访问其最终值。这是避免在模块级别编写大量代码的原因之一。它并不能真正帮助任何人在自动生成的文档中查看循环变量,在他们从调试器中使用您的模块时调用dir(),以及诸如此类的事情。
  • 那么,如果我理解正确的话,id 变量出现在第二个代码段上没有问题,主要是因为它被重新初始化了?也就是说,如果在第一个代码段结束后立即调用 id 而不对其进行初始化,它将保持在第一个代码段的第一个循环结束时获得的任何值??
  • @hask.duke: 是的,您可以在循环结束后立即访问循环变量,它保存在循环的最后一次重复中的任何值。虽然作为一种特殊情况,如果循环执行零次,因为data.items() 返回一个空列表或其他什么,那么循环变量根本不会被分配,所以如果这是函数中的第一次使用,那么它不是已定义。
  • 完美。非常感谢您做出如此急需的澄清。
【解决方案2】:

一般来说,缩进的块不会开始新的作用域。只有模块、类和函数定义了新的范围。 (嗯,差不多。在 Python 3 中,列表/集合/字典推导式中的索引是推导式的本地索引。)

例如,在您的示例中,archive 位于发生 with 语句的整个模块/类/函数的范围内,任何首先分配给 with 语句主体的变量也是如此。如果with 语句在模块范围内,则所有分配都将分配给模块全局变量。如果它在类定义的顶层,它们都是类属性。如果它(很可能)在函数或方法声明中定义,那么它们是该函数的局部变量。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-10-16
    • 1970-01-01
    • 2013-08-30
    • 1970-01-01
    • 2017-11-30
    • 2013-06-12
    • 1970-01-01
    相关资源
    最近更新 更多