問題描述
編輯:
在這個問題的底部查看我的完整答案.
tl;dr answer:Python 具有靜態嵌套的作用域.靜態aspect 可以與隱式變量聲明交互,產生不明顯的結果.
(這可能特別令人驚訝,因為該語言通常具有動態特性).
我認為我對 Python 的范圍規則有很好的處理,但這個問題讓我徹底陷入困境,我的 google-fu 讓我失望了(我并不感到驚訝 - 看看問題標題;)
我將從幾個按預期工作的示例開始,但請隨意跳到示例 4 了解更多內容.
示例 1.
>>>x = 3>>>我的類(對象):... x = x...>>>我的類.x3
直截了當:在類定義期間,我們能夠訪問在外部(在本例中為全局)范圍中定義的變量.
示例 2.
>>>定義我的方法(自我):...返回self.x...>>>x = 3>>>我的類(對象):... x = x... 我的方法 = 我的方法...>>>MyClass().mymethod()3
再一次(暫時忽略為什么人們可能想要這樣做),這里沒有什么意外的:我們可以訪問外部范圍內的函數.
注意:正如 Frédéric 在下面指出的,此功能似乎不起作用.請參閱示例 5(及以后).
示例 3.
>>>定義我的函數():... x = 3...類MyClass(對象):... x = x...返回我的班級...>>>myfunc().x回溯(最近一次通話最后):<module> 中的文件<stdin>"第 1 行myfunc 中的文件<stdin>",第 3 行MyClass 中的文件<stdin>"第 4 行NameError:名稱x"未定義
這與示例 1 基本相同:我們從類定義中訪問外部作用域,只是這一次該作用域不是全局的,這要感謝 myfunc()
.
編輯 5:正如 @user3022222 在下面指出的,我在我的原帖.我相信這會失敗,因為只有函數(而不是其他代碼塊,如此類定義)可以訪問封閉范圍內的變量.對于非功能代碼塊,只能訪問局部變量、全局變量和內置變量.這個問題
還有一個:
示例 4.
>>>def my_defining_func():...定義我的方法(自我):...返回self.y...類MyClass(對象):... 我的方法 = 我的方法... y = 3...返回我的班級...>>>我的定義函數()回溯(最近一次通話最后):<module> 中的文件<stdin>"第 1 行文件<stdin>",第 4 行,在 my_defining_func 中MyClass 中的文件<stdin>",第 5 行NameError:名稱mymethod"未定義
嗯...打擾一下?
這與示例 2 有何不同?
我完全糊涂了.請給我整理一下.謝謝!
附:如果這不僅僅是我理解的問題,我已經在 Python 2.5.2 和 Python 2.6.2 上嘗試過.不幸的是,這些是我目前可以訪問的所有內容,但它們都表現出相同的行為.
編輯根據 http://docs.python.org/tutorial/classes.html#python-scopes-and-namespaces:在執行過程中的任何時候,至少有三個嵌套作用域的命名空間是可以直接訪問的:
- 最里面的范圍,即首先搜索,包含本地名字
- 任何封閉的范圍搜索到的函數從最近的封閉開始范圍,包含非本地的,但也非全局名稱
- 倒數第二個范圍包含當前模塊的全局名稱
- 最外層范圍(最后搜索)是包含內置的命名空間名字
#4.似乎是其中第二個的反例.
編輯 2
示例 5.
>>>定義樂趣1():... x = 3...定義樂趣2():... 打印 x...返回樂趣2...>>>樂趣1()()3
編輯 3
正如@Frédéric 指出的,分配給與外部范圍內同名的變量似乎掩蓋"了外部變量,從而阻止了分配功能.
所以示例 4 的這個修改版本有效:
def my_defining_func():def mymethod_outer(self):返回self.y我的類(對象):mymethod = mymethod_outery = 3返回我的班級我的定義函數()
但事實并非如此:
def my_defining_func():定義我的方法(自我):返回self.y我的類(對象):mymethod_temp = 我的方法mymethod = mymethod_tempy = 3返回我的班級我的定義函數()
我仍然不完全理解為什么會發生這種屏蔽:分配發生時不應該發生名稱綁定嗎?
這個例子至少提供了一些提示(以及更有用的錯誤信息):
>>>def my_defining_func():... x = 3... def my_inner_func():... x = x...返回 x...返回 my_inner_func...>>>my_defining_func()()回溯(最近一次通話最后):<module> 中的文件<stdin>"第 1 行文件<stdin>",第 4 行,在 my_inner_funcUnboundLocalError:分配前引用的局部變量x">>>我的定義函數()<函數 my_inner_func 在 0xb755e6f4>
所以看起來局部變量是在函數創建時定義的(成功),導致局部名稱被保留",從而在調用函數時屏蔽了外部范圍名稱.
有趣.
感謝 Frédéric 的回答!
供參考,來自 python 文檔:
<塊引用>認識到范圍很重要由文本確定:全球a 中定義的函數的范圍module 是該模塊的命名空間,否來自哪里或以什么別名函數被調用.另一方面,名稱的實際搜索已完成在運行時動態地——然而,語言定義正在演變走向靜態名稱解析,在編譯"時間,所以不要依賴動態名稱解析!(實際上,局部變量已經確定靜態的.)
編輯 4
真正的答案
這種看似令人困惑的行為是由 Python 的 PEP 227 中定義的靜態嵌套范圍.它實際上與 PEP 3104 無關.
來自 PEP 227:
<塊引用>名稱解析規則是典型的對于靜態范圍的語言 [...][except] 變量未聲明.如果發生名稱綁定操作函數中的任何位置,然后是該名稱被視為函數的本地所有參考資料均指當地捆綁.如果引用發生在之前名稱是綁定的,一個 NameError 是提出來.
[...]
蒂姆·彼得斯 (Tim Peters) 的一個例子展示了沒有聲明的嵌套范圍:
i = 6定義 f(x):定義 g():打印我# ...# 跳到下一頁# ...for i in x: # ah, i *is* 是 f 的局部變量,所以這是 g 看到的經過G()
對 g() 的調用將引用由 for 綁定在 f() 中的變量 i環形.如果在循環執行之前調用 g(),NameError 將被撫養.
讓我們運行兩個更簡單的 Tim 示例:
>>>我 = 6>>>定義 f(x):... 定義 g():...打印我... # ...... # 之后... # ...... 我 = x... G()...>>>f(3)3
當 g()
在其內部范圍內沒有找到 i
時,它會動態向外搜索,在 中找到
的作用域,已經通過i
fi = x
賦值綁定到3
.
但是更改f
中最后兩條語句的順序會導致錯誤:
>>>我 = 6>>>定義 f(x):... 定義 g():...打印我... # ...... # 之后... # ...... G()... i = x # 注意:我交換了位置...>>>f(3)回溯(最近一次通話最后):<module> 中的文件<stdin>"第 1 行文件<stdin>",第 7 行,在 f文件<stdin>",第 3 行,以 g 為單位NameError:在封閉范圍內賦值之前引用了自由變量i"
記住 PEP 227 說過名稱解析規則是靜態范圍語言的典型規則",讓我們看看(半)等效的 C 版本提供:
//nested.c#include <stdio.h>詮釋 i = 6;無效 f(int x){詮釋我;//<--- 隱含在上面的python代碼中無效 g(){printf("%d
",i);}G();我 = x;G();}詮釋主要(無效){f(3);}
編譯運行:
$ gcc nested.c -o 嵌套$ ./嵌套1345208203
因此,盡管 C 會愉快地使用未綁定的變量(使用之前存儲的任何變量:134520820,在這種情況下),但 Python(謝天謝地)拒絕了.
作為一個有趣的旁注,靜態嵌套范圍可以實現 Alex Martelli 稱Python 編譯器所做的最重要的優化:函數的局部變量不保存在 dict 中,它們位于緊密的值向量中,并且每個局部變量都可以訪問使用該向量中的索引,而不是名稱查找."
這是 Python 名稱解析規則的產物:你只能訪問全局和局部范圍,但不能訪問中間的范圍,例如不要在你的直接外部范圍內.
以上措辭不佳,您確實可以訪問外部范圍中定義的變量,但是通過執行 x = x
或 mymethod = mymethod
來自非全局命名空間,您實際上是在用您在本地定義的變量掩蓋外部變量.
在示例 2 中,您的直接外部范圍是全局范圍,因此 MyClass
可以看到 mymethod
,但在示例 4 中,您的直接外部范圍是 my_defining_func()
,所以它不能,因為 mymethod
的外部定義已經被它的本地定義所掩蓋.
有關非本地名稱解析的更多詳細信息,請參閱 PEP 3104.p>
還要注意,由于上述原因,我無法讓示例 3 在 Python 2.6.5 或 3.1.2 下運行:
>>>定義我的函數():... x = 3...類MyClass(對象):... x = x...返回我的班級...>>>myfunc().x回溯(最近一次通話最后):<module> 中的文件<stdin>"第 1 行myfunc 中的文件<stdin>",第 3 行MyClass 中的文件<stdin>"第 4 行NameError:名稱x"未定義
但以下方法可行:
>>>定義我的函數():... x = 3...類MyClass(對象):... y = x...返回我的班級...>>>myfunc().y3
Edit:
See my full answer at the bottom of this question.
tl;dr answer: Python has statically nested scopes. The static aspect can interact with the implicit variable declarations, yielding non-obvious results.
(This can be especially surprising because of the language's generally dynamic nature).
I thought I had a pretty good handle on Python's scoping rules, but this problem has me thoroughly stymied, and my google-fu has failed me (not that I'm surprised - look at the question title ;)
I'm going to start with a few examples that work as expected, but feel free to skip to example 4 for the juicy part.
Example 1.
>>> x = 3
>>> class MyClass(object):
... x = x
...
>>> MyClass.x
3
Straightforward enough: during class definition we're able to access the variables defined in the outer (in this case global) scope.
Example 2.
>>> def mymethod(self):
... return self.x
...
>>> x = 3
>>> class MyClass(object):
... x = x
... mymethod = mymethod
...
>>> MyClass().mymethod()
3
Again (ignoring for the moment why one might want to do this), there's nothing unexpected here: we can access functions in the outer scope.
Note: as Frédéric pointed out below, this function doesn't seem to work. See Example 5 (and beyond) instead.
Example 3.
>>> def myfunc():
... x = 3
... class MyClass(object):
... x = x
... return MyClass
...
>>> myfunc().x
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 3, in myfunc
File "<stdin>", line 4, in MyClass
NameError: name 'x' is not defined
This is essentially the same as example 1: we're accessing the outer scope from within the class definition, just this time that scope isn't global, thanks to myfunc()
.
Edit 5: As @user3022222 pointed out below, I botched this example in my original posting. I believe this fails because only functions (not other code blocks, like this class definition) can access variables in the enclosing scope. For non-function code blocks, only local, global and built-in variables are accessible. A more thorough explanation is available in this question
One more:
Example 4.
>>> def my_defining_func():
... def mymethod(self):
... return self.y
... class MyClass(object):
... mymethod = mymethod
... y = 3
... return MyClass
...
>>> my_defining_func()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 4, in my_defining_func
File "<stdin>", line 5, in MyClass
NameError: name 'mymethod' is not defined
Um...excuse me?
What makes this any different from example 2?
I'm completely befuddled. Please sort me out. Thanks!
P.S. on the off-chance that this isn't just a problem with my understanding, I've tried this on Python 2.5.2 and Python 2.6.2. Unfortunately those are all I have access to at the moment, but they both exhibit the same behaviour.
Edit According to http://docs.python.org/tutorial/classes.html#python-scopes-and-namespaces: at any time during execution, there are at least three nested scopes whose namespaces are directly accessible:
- the innermost scope, which is searched first, contains the local names
- the scopes of any enclosing functions, which are searched starting with the nearest enclosing scope, contains non-local, but also non-global names
- the next-to-last scope contains the current module’s global names
- the outermost scope (searched last) is the namespace containing built-in names
#4. seems to be a counter-example to the second of these.
Edit 2
Example 5.
>>> def fun1():
... x = 3
... def fun2():
... print x
... return fun2
...
>>> fun1()()
3
Edit 3
As @Frédéric pointed out the assignment of to a variable of the same name as it has in the outer scope seems to "mask" the outer variable, preventing the assignment from functioning.
So this modified version of Example 4 works:
def my_defining_func():
def mymethod_outer(self):
return self.y
class MyClass(object):
mymethod = mymethod_outer
y = 3
return MyClass
my_defining_func()
However this doesn't:
def my_defining_func():
def mymethod(self):
return self.y
class MyClass(object):
mymethod_temp = mymethod
mymethod = mymethod_temp
y = 3
return MyClass
my_defining_func()
I still don't fully understand why this masking occurs: shouldn't the name binding occur when the assignment happens?
This example at least provides some hint (and a more useful error message):
>>> def my_defining_func():
... x = 3
... def my_inner_func():
... x = x
... return x
... return my_inner_func
...
>>> my_defining_func()()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 4, in my_inner_func
UnboundLocalError: local variable 'x' referenced before assignment
>>> my_defining_func()
<function my_inner_func at 0xb755e6f4>
So it appears that the local variable is defined at function creation (which succeeds), resulting in the local name being "reserved" and thus masking the outer-scope name when the function is called.
Interesting.
Thanks Frédéric for the answer(s)!
For reference, from the python docs:
It is important to realize that scopes are determined textually: the global scope of a function defined in a module is that module’s namespace, no matter from where or by what alias the function is called. On the other hand, the actual search for names is done dynamically, at run time — however, the language definition is evolving towards static name resolution, at "compile" time, so don’t rely on dynamic name resolution! (In fact, local variables are already determined statically.)
Edit 4
The Real Answer
This seemingly confusing behaviour is caused by Python's statically nested scopes as defined in PEP 227. It actually has nothing to do with PEP 3104.
From PEP 227:
The name resolution rules are typical for statically scoped languages [...] [except] variables are not declared. If a name binding operation occurs anywhere in a function, then that name is treated as local to the function and all references refer to the local binding. If a reference occurs before the name is bound, a NameError is raised.
[...]
An example from Tim Peters demonstrates the potential pitfalls of nested scopes in the absence of declarations:
i = 6 def f(x): def g(): print i # ... # skip to the next page # ... for i in x: # ah, i *is* local to f, so this is what g sees pass g()
The call to g() will refer to the variable i bound in f() by the for loop. If g() is called before the loop is executed, a NameError will be raised.
Lets run two simpler versions of Tim's example:
>>> i = 6
>>> def f(x):
... def g():
... print i
... # ...
... # later
... # ...
... i = x
... g()
...
>>> f(3)
3
when g()
doesn't find i
in its inner scope, it dynamically searches outwards, finding the i
in f
's scope, which has been bound to 3
through the i = x
assignment.
But changing the order the final two statements in f
causes an error:
>>> i = 6
>>> def f(x):
... def g():
... print i
... # ...
... # later
... # ...
... g()
... i = x # Note: I've swapped places
...
>>> f(3)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 7, in f
File "<stdin>", line 3, in g
NameError: free variable 'i' referenced before assignment in enclosing scope
Remembering that PEP 227 said "The name resolution rules are typical for statically scoped languages", lets look at the (semi-)equivalent C version offer:
// nested.c
#include <stdio.h>
int i = 6;
void f(int x){
int i; // <--- implicit in the python code above
void g(){
printf("%d
",i);
}
g();
i = x;
g();
}
int main(void){
f(3);
}
compile and run:
$ gcc nested.c -o nested
$ ./nested
134520820
3
So while C will happily use an unbound variable (using whatever happens to have been stored there before: 134520820, in this case), Python (thankfully) refuses.
As an interesting side-note, statically nested scopes enable what Alex Martelli has called "the single most important optimization the Python compiler does: a function's local variables are not kept in a dict, they're in a tight vector of values, and each local variable access uses the index in that vector, not a name lookup."
That's an artifact of Python's name resolution rules: you only have access to the global and the local scopes, but not to the scopes in-between, e.g. not to your immediate outer scope.
EDIT: The above was poorly worded, you do have access to the variables defined in outer scopes, but by doing x = x
or mymethod = mymethod
from a non-global namespace, you're actually masking the outer variable with the one you're defining locally.
In example 2, your immediate outer scope is the global scope, so MyClass
can see mymethod
, but in example 4 your immediate outer scope is my_defining_func()
, so it can't, because the outer definition of mymethod
is already masked by its local definition.
See PEP 3104 for more details about nonlocal name resolution.
Also note that, for the reasons explained above, I can't get example 3 to run under either Python 2.6.5 or 3.1.2:
>>> def myfunc():
... x = 3
... class MyClass(object):
... x = x
... return MyClass
...
>>> myfunc().x
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 3, in myfunc
File "<stdin>", line 4, in MyClass
NameError: name 'x' is not defined
But the following would work:
>>> def myfunc():
... x = 3
... class MyClass(object):
... y = x
... return MyClass
...
>>> myfunc().y
3
這篇關于在函數中創建一個類并訪問在包含函數的范圍內定義的函數的文章就介紹到這了,希望我們推薦的答案對大家有所幫助,也希望大家多多支持html5模板網!