Python: Common Newbie Mistakes, Part 2
2013-12-30 01:10
453 查看
Scoping
The focus of this part is an area of problems where scoping in Python is misunderstood. Usually, when we have global variables (okay, I’ll say it because I have to – global variables are bad), Python understands it if we access them within a function:1 2 3 | bar = 42 def foo(): print bar |
foo, a global
variable called
barand it works as
expected:
1 2 | >>> foo() 42 |
1 2 34 | bar = [42] def foo(): bar.append(0)foo() >>> print bar [42, 0] |
bar?
1 2 34 | >>> bar = 42 ... def foo(): ... bar = 0 ... foo() ... print bar 42 |
fooran fine and without
exceptions, but if we print the value of
barwe’ll
see that it’s still
42! What happens
here is that the line
bar = 0, instead
of changing
bar, created a new, local
variable also called
barand set its
value to
0. This is a tough bug to
find and it causes some grief to newbies (and veterans!) who aren’t really sure of how Python’s scoping works. To understand when and how Python decided to treat variables as global or local, let’s look at a less common, but probably more baffling version
of this mistake and add an assignment to
barafter
we print it:
1 2 34 | bar = 42 def foo(): print barbar = 0 |
1 2 34 | >>> foo() Traceback (most recent call last): File "<pyshell#4>", line 1, in <module> foo() File "<pyshell#3>", line 3, in foo print bar UnboundLocalError: local variable ''bar'' referenced before assignment |
How is this possible? Well, there are two parts to this misunderstanding. The first misconception is that Python, being an interpreted language (which is awesome, I think we can all agree), is executed line-by-line. In truth, Python is being executed statement-by-statement.
To get a feel of what I mean, go to your favorite shell (you
aren’t using the default one, I hope) and type the following:
1 | def foo(): |
is a statement. Well, it’s a compound statements, that includes within it many other statements, but a statement notwithstanding. The content of your function isn’t being executed until you actually call it. What is being executed is that a function
object is being created.
This leads us to the second point. Again, Python’s dynamic and interpreted nature leads us to believe that when the line
print baris executed, Python will look for a variable
barfirst
in the local scope and then in the global scope. What really happens here is that the local scope is in fact not completely dynamic. When the
defstatement
is executed, Python statically gathers information regarding the local scope of the function. When it reaches the line
bar = 0(not when it executes it, but when it reads the function definition), it adds
“bar”to
the list of local variable for
foo.
When
foois executed and Python tries
to execute the line print bar, it looks for the variable in the local scope and it finds it, since it was statically accessed, but it knows that it wasn’t assigned yet – it has no value. So the exception is raised.
You could ask “why couldn’t an exception be raised when we were declaring the function? Python could have known in advance that
barwas
referenced before assignment”. The answer to that is that Python can’t know whether the local
barwas
assigned to or not. Look at the following:
1 2 34 | bar = 42 def foo(baz): if baz > 0: print bar bar = 0 |
baris
assigned to, but it doesn’t know it’s referenced before assignment until it actually happens. Wait – in fact, it doesn’t even know it was assigned to!
1 2 34 | bar = 42 def foo(): print barif False: |
foo, we get:
1 2 34 | Traceback (most recent call last): File "<pyshell#17>", line 1, in <module> foo() File "<pyshell#16>", line 3, in foo print bar UnboundLocalError: local variable 'bar' referenced before assignment |
barwill
never happen, Python ignores that fact and still declares
baras
statically local.
I’ve been babbling about the problem long enough. We want solutions, baby! I’ll give you two.
1 2 34 | >>> bar = 42 |
globalkeyword.
It’s pretty self-explanatory. It let’s Python know that
baris
a global variable and not local.
The second, more preferred solution, is – don’t. In the sense of – don’t use a global that isn’t constant. In my day-to-day I work on a lot of Python code and there isn’t one use of the
globalkeyword.
It’s nice to know about it, but in the end it’s avoidable. If you want to keep a value that is used throughout your code, define it as a class attribute for a new class. That way the
globalkeyword
is redundant since you need to qualify your variable access with its class name:
1 2 34 | >>> class Baz(object): |
相关文章推荐
- Python: Common Newbie Mistakes, Part 1
- 使用Python进行稳定可靠的文件操作
- python:常用功能之文本处理
- python搭建personal-blog
- 不同程序语言之间的互动,IronPython 与C#交互
- python登录豆瓣,发帖
- python之模拟鼠标键盘动作具体实现
- Python抓取Discuz!用户名脚本代码
- python IDE 集合
- python笔记-MySQLdb模块的使用笔记
- Python转码问题的解决方法
- Pyton实现简单爬虫和正则表达式的利用
- 第一次用python 写的简单爬虫 记录在自己的博客
- python 启动多个php进程处理数据
- python socket
- python for android : 一个简单文件浏览器的实例 play mp3
- 利用python实战开发一个web管理系统框架
- wx.Frame 技巧小结
- 用Python建立最简单的web服务器
- Python实现系统桌面时钟