如何在Python中强制本地范围?

时间:2014-03-04 05:11:15

标签: python scope

在C ++中,您可以这样做以强制本地范围:

{
    int i = 1;
    // Do stuff
}
// local variable i is destroyed
{
    int i = 7;
    // Do more stuff
}

这样做的好处是,在强制本地范围结束时,括号中声明的任何变量都消失了。这有助于防止在以后您不打算使用x的位置使用先前定义的变量x。

你能用Python做到这一点吗?如果是这样,怎么样?

== UPDATE ==

我知道功能 - 这是显而易见的事情。我想知道当代码很简单并且不值得创建单独的函数时,是否有一种快速的方法来执行上述操作 - 只需要一些快速符号来强调此块中的变量不会在其他任何地方使用功能

到目前为止,人们所说的简短回答是否定的。

(我知道有一些聪明的方法,比如“del”,或者这种有块的愿望可能会建议重构成一个单独的函数。但是我想强调这只是为了简短的片段你要强调的这个小块中的变量不能在别处使用。)

5 个答案:

答案 0 :(得分:7)

在Python中,如果在函数内部声明变量,则它是本地的,不能在函数外部访问

>>> def x():
    i = 5


>>> x()
>>> i

Traceback (most recent call last):
  File "<pyshell#5>", line 1, in <module>
    i
NameError: name 'i' is not defined
>>> 

或者,您可以从末尾的命名空间中删除该变量,以便您不能重复使用它。

>>> i = 5
>>> del i
>>> i

Traceback (most recent call last):
  File "<pyshell#8>", line 1, in <module>
    i
NameError: name 'i' is not defined
>>> 

答案 1 :(得分:1)

如果您不喜欢del解决方案,可以嵌套函数定义:

def one_function():
    x=0
    def f():
        x = 1
    f()
    print(x) # 0

当然,我认为更好的方法是将事物分成更小的函数,因此不需要这个手动范围。在C ++中,关于它的最酷的事情就是自动调用析构函数 - 在Python中,你不能真正保证析构函数将被调用,所以即使可能,这个作用域也不会非常有用。

答案 2 :(得分:1)

在C ++中,您使用带括号{}的本地范围来避免变量重定义或命名冲突:

{
    int var=3;
}
{
    float var=1.0f;
}

在python中,没有显式变量 definition ,只需在想要使用它时将一些对象分配给var名称,并将相同名称重新绑定到一些新变量:

var=3
#do something
var=1.0  #no need to "del var", var refers to a float object now
#do more stuff

注意在C ++中使用范围块可能表明您的代码需要重构为可以命名和重用的函数或方法。和python一样。

答案 3 :(得分:0)

我有同样的问题,发现您绝对可以

它不像c样式块那么干净,但是通过两次python怪癖,我们可以使其达到我们的目的。

怪癖:

  1. 即使类从未使用过,类中包含的所有代码也会立即运行。
  2. 您可以根据需要多次重复使用类的名称。

这是您的示例:

class DoStuff:
    i = 1
    # Do stuff

# local variable i is destroyed

class DoStuff:
    i = 7
    # Do more stuff
# local variable i is destroyed

要在此处充分体现灵活性,请参见以下示例。我将类命名为“ Scope”,因为这可能是我将其与其他命名类区分开的名称。 注意,“范围”当然可以是任何东西。

我建议您在整个项目中使用一个名称,并将该名称添加到您的文档中,以便使您理解这是一个永远不应实例化的特殊名称。

outer = 1

class Scope:
    inner = outer
    print("runs first ---")
    print("outer %d" % outer)
    print("inner %d" % inner)

class Scope:
    inner = outer + 1
    print("runs second ---")
    print("outer %d" % outer)
    print("inner %d" % inner)

print("runs last ---")
print("outer %d" % outer)
print("inner %d" % inner) # This will give an error. Inner does not exist in this scope!

输出:

runs first ---
outer 1
inner 1
runs second ---
outer 1
inner 2             
runs last ---
outer 1
Traceback (most recent call last):
  File "test.py", line 18, in <module>
    print("inner %d" % inner) # This will give an error. Inner does not exist in this scope!
NameError: name 'inner' is not defined

这是可行的-让我们看一下好处/缺点的权衡。

好处:

  1. 代码保持线性,遵循代码流程,逻辑上不需要不必要的飞跃。这种线性关系将使新手更容易阅读和理解一段代码的实际作用。
  2. 代码正在向将来的编码人员自我记录,该代码仅在一个地方使用,从而使编辑更容易,因为编码人员无需进行不必要的搜索即可查找其他实例。

缺点:

  1. 我们正在使用Python的怪癖来完成这项工作,并且我感觉这种限制范围而不是创建新的一次性函数的想法并不是Python程序员倾向于做的事情。这可能会导致工作场所的紧张局势,或者导致抱怨使用骇客程序,而不是遵循创建小功能的惯例,而不管某物是否被多次使用。
  2. 如果您离开项目,而新的程序员加入并看到此代码,那么他们一开始可能会感到困惑。为了设定期望,将需要一些文档,并且必须小心以确保文档中的解释保持准确。

对于所有您想限制范围的代码,我认为这都是值得的,但没有太多使用该代码的地方,或者尚不清楚如何编写通用函数来解决所有这些情况。

如果阅读此书的人感到还有其他权衡的问题,请在此处评论,我将确保它们在“缺点”部分中有代表。

有关此公约的更多讨论,John Carmack,Jonathan Blow和Casey Muratori首选。

https://news.ycombinator.com/item?id=12120752

答案 4 :(得分:0)

我已承诺用诡计解决这个问题。

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)

https://github.com/l74d/scoping

顺便说一下,我发现dummy class的解决方案可能会导致内存泄漏。例如,在被覆盖的类中创建的大型 numpy 数组似乎没有通过查看内存统计信息进行垃圾收集,但这可能是一个实现相关的事情。