如何在python中创建新的作用域

如何在python中创建新的作用域,python,scope,Python,Scope,在许多语言(和地方)中,有一种很好的做法,即通过创建类似块的对象来创建局部作用域 我如何在python中实现这一点,而不出现意外的缩进错误,也不使用某种技巧为什么要在python中创建新的作用域 在其他语言中执行此操作的正常原因是变量作用域,但在python中不会发生这种情况 if True: a = 10 print a 列表理解(Python 3+)和生成器中的变量是本地变量: >>> i = 0 >>> [i+1 for i in range

在许多语言(和地方)中,有一种很好的做法,即通过创建类似块的对象来创建局部作用域


我如何在python中实现这一点,而不出现意外的缩进错误,也不使用某种技巧

为什么要在python中创建新的作用域

在其他语言中执行此操作的正常原因是变量作用域,但在python中不会发生这种情况

if True:
    a = 10

print a

列表理解(Python 3+)和生成器中的变量是本地变量:

>>> i = 0
>>> [i+1 for i in range(10)]
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
>>> i
0

但是为什么您需要这个呢?

我认为这是一个明确的信号,表明是时候创建一个新函数并重构代码了。我看不出有什么理由创建这样的新范围。有什么理由吗

def a():
    def b():
        pass
    b()

如果我只需要一些额外的缩进或正在调试,我将使用
如果为True:

在Python中,作用域有三种类型:全局、局部和类。您可以创建专门的“范围”字典以传递给exec/eval()。此外,还可以使用嵌套作用域 (在另一个函数中定义函数)。我发现这些在我所有的代码中都是足够的

正如Douglas Leeder已经说过的,在其他语言中使用它的主要原因是变量作用域,而这在Python中并不真正发生。此外,Python是我使用过的最具可读性的语言。做一些像“如果真的”这样的事情(你说你想避免)会有违可读性。在这种情况下,我认为最好的办法是将代码重构为多个函数,或者使用单个作用域。我认为Python中可用的作用域足以覆盖所有可能发生的情况,因此本地作用域应该不是必需的


我希望这会有所帮助。

如果您只想创建临时变量,并在使用它们之后立即对它们进行垃圾收集,那么您可以使用

del varname
当你不再需要它们的时候

如果只是为了美观,您可以使用注释或额外的换行符,但不需要额外的缩进

范围是对象的文本区域 名称空间为 可直接访问。“直接地 这里的“可访问”意味着 对名称的非限定引用 尝试在中查找名称 命名空间

请阅读并澄清您的问题


顺便说一句,如果C中的(TRUE){},您不需要
,一个简单的
{}
就足够了

Python正好有两个作用域,本地和全局。函数中使用的变量都在局部范围内,无论它们是在什么缩进级别创建的。调用嵌套函数将产生您想要的效果

def foo():
  a = 1

  def bar():
    b = 2
    print a, b #will print "1 2"

  bar()

和其他人一样,我不得不问你为什么要在函数中创建一个有限的作用域。

正如其他答案中提到的,Python中没有类似的功能来创建一个带有块的新作用域,但是在编写脚本或Jupyter笔记本时,我经常(ab)使用类引入新的名称空间以获得类似的效果。例如,在一个笔记本中,您可能有一个模型“Foo”、“Bar”等和相关变量,您可能希望创建一个新的作用域,以避免重复使用诸如

model = FooModel()
optimizer = FooOptimizer()
...
model = BarModel()
optimizer = BarOptimizer()
model_foo = ...
optimizer_foo = ...

model_bar = ...
optimizer_bar= ...
或后缀名称,如

model = FooModel()
optimizer = FooOptimizer()
...
model = BarModel()
optimizer = BarOptimizer()
model_foo = ...
optimizer_foo = ...

model_bar = ...
optimizer_bar= ...
相反,您可以使用

class Foo:
    model = ...
    optimizer = ...
    loss = ....

class Bar:
    model = ...
    optimizer = ...
    loss = ...
然后访问变量作为

Foo.model
Bar.optimizer
...

我发现,以这种方式使用名称空间来创建新的作用域可以使代码更具可读性,更不容易出错。

与此类似,对于任意名称
t

### at top of function / script / outer scope (maybe just big jupyter cell)
try: t
except NameError:
    class t
        pass
else:
    raise NameError('please `del t` first')

#### Cut here -- you only need 1x of the above -- example usage below ###

t.tempone = 5 # make new temporary variable that definitely doesn't bother anything else.
# block of calls here...
t.temptwo = 'bar' # another one...
del t.tempone # you can have overlapping scopes this way

# more calls
t.tempthree = t.temptwo; del t.temptwo # done with that now too
print(t.tempthree)
# etc, etc -- any number of variables will fit into t.


### At end of outer scope, to return `t` to being 'unused'
del t
所有这些都可以在函数def中,也可以在脚本中def之外的任何地方

您可以在任意点向任意命名类添加或删除新元素。您实际上只需要其中一个,然后根据需要管理“临时”命名空间

如果函数体中有
delt
语句,则不需要它,但如果包含它,则可以复制/粘贴彼此相隔很远的代码块,并使它们按预期方式工作(由于“t”的不同用法是完全独立的,每个用法都以
try:t..
块开始,以
delt
结束)

这样,如果
t
已经被用作变量,您会发现,它不会影响t,因此您可以发现它是什么

这比使用一系列random=named函数只调用它们一次更不容易出错,因为这样可以避免处理它们的名称,或者记住在它们的定义之后调用它们,特别是当您必须对长代码重新排序时

这基本上正是你想要的:做一个临时的地方来放置你确信不会与其他任何东西碰撞的东西,你负责在你走的时候清理里面的东西


是的,这很难看,而且可能会让人泄气——你会被指示将你的工作分解成一组更小、更可重用的函数。

正如其他人所建议的那样,python在不污染封闭名称空间的情况下执行代码的方法是将其放入类或函数中。这带来了一个轻微且通常无害的问题:定义function将其名称放在封闭的命名空间中。如果这对您造成了伤害,您可以使用Python的常规临时变量“\ux”命名函数:

这可以递归地完成,因为每个局部定义都会从封闭范围屏蔽该定义


只有在非常特定的情况下才需要这种功能。例如,使用Databricks的
%run
magic时,它会执行当前笔记本全局范围内另一个笔记本的内容。将子笔记本的命令包装在临时函数中可防止它们污染全局namespace.

虽然泄漏范围确实是一个经常有用的功能, 我创建了一个包来模拟块作用域(您可以选择选择性泄漏,通常是为了得到结果)


在其他语言中,变量作用域实际上意味着内存管理。垃圾收集器不需要这种功能。我上次检查时,CPython的垃圾收集器doe
from scoping import scoping
a = 2
with scoping():
    assert(2 == a)
    a = 3
    b = 4
    scoping.keep('b')
    assert(3 == a)
assert(2 == a)
assert(4 == b)