每一个变量都会过好它们的一生
暑期Stata培训班招生啦!!!接力线上的网课培训,我们在今夏又开始新一轮的线下培训啦!8月4日至12日,爬虫俱乐部期待与您的相遇!培训具体内容详见推文《暑期Stata编程技术定制培训班》。
有问题,不要怕!点击推文底部“阅读原文”下载爬虫俱乐部用户问题登记表并按要求填写后发送至邮箱statatraining@163.com,我们会及时为您解答哟~
喜大普奔~爬虫俱乐部的github主站正式上线了!我们的网站地址是:https://stata-club.github.io,粉丝们可以通过该网站访问过去的推文哟~
好消息:爬虫俱乐部即将推出研究助理供需平台,如果您需要招聘研究助理(Research Assistant or Research Associate),可以将您的需求通过我们的公众号发布;如果您想成为一个RA,可以将您的简历发给我们,进入我们的研究助理数据库。帮我们写优质的推文可以提升您被知名教授雇用的胜算呀!
今天我们的python推文不讲案例也不讲新的标准库,来和大家好好聊一聊一个看似乏味,却十分关键又有用的问题————变量的作用域。
什么叫变量作用域呢?简而言之就是变量有效的范围,出了这个范围,变量就会无效,也就是生命周期结束。Python中函数、类以及模块下的变量都有其特殊的作用范围,为了简便起见,今天我们只讨论函数情形下的变量作用域问题。
首先我们看一段示例代码
def var_gen(a):
func_var = 404
return a + 10
b = var_gen(10)
try:
print(func_var)
except Exception as e:
print(e)
我们会发现,该段代码的输出为name 'func_var' is not defined,也就是说我们的程序在执行try时出了错,错误原因是func_var变量没有定义。可是我们在函数var_gen()中明明已经定义了呀,并且也执行了一遍函数。这是因为在函数体内定义的变量,其作用域范围仅为从声明该变量开始,到函数体执行结束,当函数执行完毕后,在该函数内定义的变量全部失效。
global关键字
那么如果我们想实现一个功能,当程序执行一个函数,则在函数体内部计数一个执行结果,并且执行结果随着函数调用而累计,应该怎么做呢?我们使用global关键字
def count_cal(a):
global count
count = count + len(a)
count = 0
count_cal("spring")
count_cal("summer")
count_cal("spring")
count_cal("summer")
print(count)
在这个示例代码中,count_cal用于计数传进来的字符串长度,且该值叠加,我们使用global关键字,声明此处的变量count是全局变量,也就是和函数体外部的count为同一变量,此时就可以实现计数累加的效果。
nonlocal关键字
除了global,还有没有神奇的关键词用以实现某些必须跨越变量作用域的功能呢?那就是nonlocal,nonlocal表示申明此处为一个闭合的作用域中的变量,现在我们将问题变得复杂一点,来看一看以下这段代码
def scope_test():
def do_nonlocal():
nonlocal spam
spam = "nonlocal_spam"
def do_global():
global spam
spam = "global_spam"
spam = "test_spam"
do_nonlocal()
print("After_nonlocal_assignment:", spam)
do_global()
print("After_global_assignment:", spam)
scope_test()
print("In_global_scope:", spam)
这个写法有些奇怪,我们首先定义了一个函数scope_test(),紧接着在该函数内定义两个函数do_nonlocal和do_global,这并不会引发语法错误,在函数体内执行函数定义,这在python中是合法的,它与平级定义若干个函数这个方法在功能上大部分一样,但是在变量作用域上则会有区别。
它的运行结果是
After_nonlocal_assignment: nonlocal_spam
After_global_assignment: nonlocal_spam
In_global_scope: global_spam
这是因为,nonlocal spam成功申明到了函数scope_test内的spam变量,原因是spam变量处于scope_test函数内,是在一个闭合作用域内的变量。而global未申明到spam变量,而是直接在函数体外创造了一个全局变量spam,原因是global用于申明全局变量,与scope_test函数内的spam不一致。所以我们看到了,在第二行结果中,未申明成功,spam的内容依旧是上一次的nonlocal_spam,而在全局范围内的输出成功了。
函数的参数列表传递
在看完了上面的两个关键字后,我们在本文的最后讨论一个问题,也是python的初学者们经常感到困惑的一个问题,也就是函数的参数列表传递,请看下面一段代码
def getlist(alist = []):
alist.append('a')
return alist
def getint(a = 10):
a = a + 10
return a
print(getint())
print(getint())
print(getint(5))
print(getint(1))
print(getlist())
print(getlist())
print(getlist())
print(getlist())
这里我们定义了两个函数,一个是getint用以取得变量a,一个是getlist用以取得列表alist。它们的运行结果可能会出乎你的意料
20
20
15
11
['a']
['a', 'a']
['a', 'a', 'a']
['a', 'a', 'a', 'a']
getint看似正常运行了,我们不传递参数时,函数使用默认值10,得到值20.。而getlist得到的列表却在增加——alist=[]这句默认参数只在最初执行了一遍。这是因为对于函数的默认列表而言,是函数作为一个整体概念与其默认列表一一对应,当我们直接将默认列表拿来append后,原有的默认列表就会被修改,且存在累加。再强调一遍,函数的默认列表是与函数这个整体做绑定,而并非是某一次调用函数的产物。如上所示的代码写法是非常危险且容易出错的,因为我们不知道自己曾经对这个默认参数列表做过什么,因此更为稳妥的写法应该是
def getlist(alist = []):
res = list(alist)
res.append('a')
return res
也就是将默认参数列表复制一份,在复制的版本上进行操作,不污染原有的默认参数列表。
结语
每一个变量都有其固有的作用域范围和生命周期,一个优秀的开发者清楚地知道不让任何一个变量不合时宜的越界,如果不得已使用一些关键字的特殊方法,也需要清楚地知道它们都会发生什么,我们在文章中会展示一些“危险的写法”,也许你会对它们的神奇特性感到心痒,想利用它们做一些特别的操作,但这恰恰是我们不提倡做的,“规范永远比效率更重要,某种程度上,规范本身就是效率”。每个变量都会过好它的一生,将它们合理地安排好,我们才能让程序富有效率地完成它的使命。
注:此推文中的图片及封面(除操作部分的)均来源于网络!如有雷同,纯属巧合!
以上就是今天给大家分享的内容了,说得好就赏个铜板呗!有钱的捧个钱场,有人的捧个人场~。另外,我们开通了苹果手机打赏通道,只要扫描下方的二维码,就可以打赏啦!
应广大粉丝要求,爬虫俱乐部的推文公众号打赏功能可以开发票啦,累计打赏超过1000元我们即可给您开具发票,发票类别为“咨询费”。用心做事,只为做您更贴心的小爬虫。第一批发票已经寄到各位小主的手中,大家快来给小爬虫打赏呀~
文字编辑:赵宇亮
技术总编:刘贝贝
往期推文推荐:
2.hello,MySQL--Stata连接MySQL数据库
3.hello,MySQL--odbcload读取MySQL数据
9.想看什么书?Stata君帮你寻!——爬取中南财大图书馆书目信息
关于我们
微信公众号“爬虫俱乐部”分享实用的stata命令,欢迎转载、打赏。爬虫俱乐部是由李春涛教授领导下的研究生及本科生组成的大数据分析和数据挖掘团队。
此外,欢迎大家踊跃投稿,介绍一些关于stata的数据处理和分析技巧。
投稿邮箱:statatraining@163.com
投稿要求:
1)必须原创,禁止抄袭;
2)必须准确,详细,有例子,有截图;
注意事项:
1)所有投稿都会经过本公众号运营团队成员的审核,审核通过才可录用,一经录用,会在该推文里为作者署名,并有赏金分成。
2)邮件请注明投稿,邮件名称为“投稿”+“推文名称”。
3)应广大读者要求,现开通有偿问答服务,如果大家遇到关于stata分析数据的问题,可以在公众号中提出,只需支付少量赏金,我们会在后期的推文里给予解答。