文档章节

Python 内层名字空间访问外层名字空间中的变量

Konghy
 Konghy
发布于 2017/02/25 14:58
字数 945
阅读 119
收藏 0

某天,一位新来的同事问我,在 Python 的函数中,怎么定义一个像 C 语言中的 static 变量。就像这样:

void foo(void)
{
    static int a = 0;
    
    ...
}

已经抛弃 C 好长时间了,现在工作和业余都在使用 Python,也习惯用 Python 去思考。被问到这个问题时,我一时还真不知道怎么回答,因为在使用 Python 的这么长时间里,我还真没有遇到过这样的需求。当然,Python 自身也不直接支持类似的语法,所以也不会这样去思考。

实际上,他是想要在函数中保持一个变量的状态。经过短暂的思考,我回答他,你可以定义一个全局变量,然后在函数中用 global 引用这个变量就可以。顺便我还告诉他,Python 没有原生的 static 变量支持,你应该换一下别的思路,程序并不一定非要这么设计。

过了一会,他又来找我,说用 global 关键字后程序有问题。无赖之下,只能看了看他写的代码,其大概是像下面这样:

class Foo(object):
    def too(self):
        l = 1
        def moo():
            global l
            l = l + 1
            print l
        moo()
        print l

f = Foo()
f.too()

我告诉他,你把 global 那一句去掉就可以了。过了一会他又说,还是不行。这个时候,我已经有些不耐烦了,怎么就不行了呢。于是我调试了上面代码,报错:

UnboundLocalError: local variable 'l' referenced before assignment

我马上意识到我忽略了一个问题,这里是一个嵌套名字空间。我的同事并没有定义一个全局变量,而是一个局部变量,但他却尝试用 global 关键字去引用,显然得到的结果会是全局变量没有定义。所以我告诉他,去掉 global 语句。我没有意识到里边还有一个嵌套的作用域,之前从来没有没有遇到过这种问题,所以也没太注意。

如果把程序改成下边这样,则可以正常运行:

class Foo(object):
    def too(self):
        l = 1
        def moo():
            print l
        moo()
        print l

f = Foo()
f.too()

当时我并没有意识到问题出在哪里,于是查了下资料,发现了如下的一些规则

  • 内部函数,不能修改全局变量但可以访问全局变量
  • 内部函数,在尝试修改同名全局变量时,Python 会认为它是一个局部变量,并引发 UnboundLocalError
  • 在内部函数修改同名全局变量之前尝试访问变量名称(如: print l; l = l + 1),也会引发 UnboundLocalError

所以问题涉及到一个作用域层次的问题,也就是 内层作用域可以访问外层作用域中的变量,但不能修改,一旦尝试修改,Python 则会认为该变量是一个局部变量。这样,问题就被解释了。

还需要注意的一点是,在 Python 中,只有模块,类以及函数才会引入新的作用域,其它的代码块是不会引入新的作用域的,即使有不同层次的缩进。

我没有仔细看同事的代码,不知道他具体要表达什么,我只是告诉他换一种设计思路。我觉得很多时候我们都应该这样,当你发现你的设计思路不符合语言习惯的时候,那说明你的设计是不正确,何不换换别的思路呢。

© 著作权归作者所有

共有 人打赏支持
Konghy
粉丝 7
博文 22
码字总数 31889
作品 0
朝阳
程序员
私信 提问
我和Python的Py交易》》》》》》 命名空间的小弟作用域

命名空间的小弟作用域 在这要明确一个观点,在Python中万物皆对象,而变量指向的就是对象。 变量可以是 类名,函数名,储存数据的变量…… 对象可以是 类 ,被封装的一段代码(函数),数据…...

BarkingPig
2018/08/01
0
0
【函数】02、函数进阶

一、函数的返回值 In [4]: def add(x, y): ...: return x+y ...: In [5]: add(1, 2)Out[5]: 3 In [8]: def add(x, y): ...: return x+y ...: print(x+y) ...: In [9]: add(1, 2)Out[9]: 3 In......

xiexiaojun
2017/06/10
0
0
python locals和globals

locals和globals 标记一下:Dive Into Python 内容 我们先偏离一下 HTML 处理的主题, 讨论一下 Python 如何处理变量。 Python 有两个内置的函数,locals和globals, 它们提供了基于 dictionary...

henosteven
2013/02/20
0
0
python locals globals

Python有两个内置的函数,locals() 和globals(),它们提供了基于字典的访问局部和全局变量的方式。 首先,是关于名字空间的一个名词解释。是枯燥,但是很重要,所以要耐心些。Python使用叫做...

Sophia_tj
2014/08/20
0
0
Python函数属性和PyCodeObject

函数属性 python中的函数是一种对象,它有属于对象的属性。除此之外,函数还可以自定义自己的属性。注意,属性是和对象相关的,和作用域无关。 自定义属性 自定义函数自己的属性方式很简单。...

Python女神
2018/11/15
0
0

没有更多内容

加载失败,请刷新页面

加载更多

发布xxl-job executor dotnet core 执行器的实现

DotXxlJob [github][https://github.com/xuanye/DotXxlJob] xxl-job的dotnet core 执行器实现,支持XXL-JOB 2.0+ 1 XXL-JOB概述 [XXL-JOB][1]是一个轻量级分布式任务调度平台,其核心设计目标...

假正经哥哥
40分钟前
1
0
mysql 查询当天、本周,本月,上一个月的数据

今天 select * from 表名 where to_days(时间字段名) = to_days(now()); 昨天 SELECT * FROM 表名 WHERE TO_DAYS( NOW( ) ) - TO_DAYS( 时间字段名) <= 1 近7天 SELECT * FROM 表名 wher......

BraveLN
今天
3
0
Spring 事务初始化源码分析

相关文章 Spring 事务使用详解 Spring AOP 创建代理的源码解析 Spring AOP 注解方式源码解析 Spring AOP 功能使用详解 Spring 的 getBean 方法源码解析 Spring bean 创建过程源码解析 Spring...

TSMYK
今天
1
0
Android Multimedia框架总结(六)C++中MediaPlayer的C/S架构

前面几节中,都是通过java层调用到jni中,jni向下到c++层并未介绍 看下Java层一个方法在c++层 MediaPlayer后续过程 frameworks/av/media/libmedia/MediaPlayer.cpp 找一个我们之前熟悉的setDa...

天王盖地虎626
今天
3
0
【Linux】【MySQL】CentOS7安装最新版MySQL8.0.13(最新版MySQL从安装到运行)

1、前言   框框博客在线报时:2018-11-07 19:31:06   当前MySQL最新版本:8.0.13 (听说比5.7快2倍)   官方之前表示:MySQL 8.0 正式版 8.0.11 已发布,MySQL 8 要比 MySQL 5.7 快 2 ...

Code辉
今天
5
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部