{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# 4.4 变量作用域"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"<font size=\"4\">变量的作用域就是指<font color=Red>__变量的有效范围__</font>,变量按照作用范围分为两类:<font color=Red>__全局变量__</font>和<font color=Red>__局部变量__</font>。</font>"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"<font size=\"4\">在Python程序中创建、改变、查找变量名时,都是在一个保存变量名的空间中进行,我们称之为<font color=Red>__命名空间__</font>,也被称之为<font color=Red>__作用域__</font>。</font>\n",
"\n",
"<font size=\"4\">Python的作用域是<font color=Red>__静态的__</font>。</font>\n",
"\n",
"<font size=\"4\">在源代码中<font color=Red>__变量名被赋值的位置__</font>决定了该变量能被访问的范围,即Python变量的<font color=Red>__作用域__</font>由变量所在源代码中的<font color=Red>__位置__</font>决定。</font>"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"在Python中,并不是所有的语句块中都会产生作用域。只有当变量在Module(模块)、Class(类)、def(函数)中定义的时候,才会有作用域的概念。\n",
"\n",
"本节我们只讨论与函数相关的变量作用域。\n",
"\n",
"<font size=\"4\">可以简单的说,在<font color=Red>__函数外部__</font>声明的变量是<font color=Red>__全局变量__</font>,其作用域是<font color=Red>__整个文件(或模块)__</font>;在<font color=Red>__函数内部__</font>声明的变量是<font color=Red>__局部变量__</font>,其作用域是声明这个变量的<font color=Red>__函数内部__</font>,在<font color=Red>__函数外部不可以访问__</font>。</font>"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 4.4.1 局部变量"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"<font color=Red>__局部变量是__</font>在函数中定义的变量,包含在def关键字定义的语句块中。\n",
"\n",
"<font color=Red>__函数每次被调用时都会创建一个新的对象__</font>。因此,局部变量仅仅是<font color=Red>__暂时的__</font>存在,依赖创建该变量的函数是否处于活动状态。函数调用时创建,函数调用结束后销毁并释放该变量的内存。"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"赵云\n"
]
},
{
"ename": "NameError",
"evalue": "name 'general_name' is not defined",
"output_type": "error",
"traceback": [
"\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
"\u001b[0;31mNameError\u001b[0m Traceback (most recent call last)",
"\u001b[0;32m/tmp/ipykernel_198/2797078214.py\u001b[0m in \u001b[0;36m<module>\u001b[0;34m\u001b[0m\n\u001b[1;32m 5\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 6\u001b[0m \u001b[0mmyName\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;31m# 调用函数myName(),打印 赵云\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 7\u001b[0;31m \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mgeneral_name\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;31m# general_name,NameError: name 'general_name' is not defined\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m",
"\u001b[0;31mNameError\u001b[0m: name 'general_name' is not defined"
]
}
],
"source": [
"def myName():\n",
" general_name = '赵云' # 函数内部定义的局部变量\n",
" print(general_name) # 输出赵云\n",
" \n",
"\n",
"myName() # 调用函数myName(),打印 赵云\n",
"print(general_name) # general_name,NameError: name 'general_name' is not defined "
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"general_name变量是在myName()函数内部定义的,属于局部变量,只能在函数内部访问,在函数外部访问时会返回<font color=Red>__NameError__</font>异常。"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 4.4.2 全局变量"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"<font color=Red>__全局变量是__</font>在模块(文件)层次中定义的变量,每一个模块都是一个<font color=Red>__全局作用域__</font>。也就是说,在模块文件<font color=Red>__顶层__</font>声明的变量都是全局变量,其<font color=Red>__作用域是当前模块文件内__</font>,从变量定义处开始直到文件末尾都是其有效范围,包括<font color=Red>__函数外部和函数内部__</font>。\n",
"\n",
"<font face='楷体' color='red' size=2> 注意,上一句话里的“顶层”是一个逻辑概念,指函数之外、和函数定义处于同一层次,并不是一个位置概念(即文件开始处)。</font>\n",
"\n",
"全局变量在模块文件运行的过程中会<font color=Red>__一直存在__</font>,占用内存空间,一般建议尽量少定义全局变量。"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"赵云\n",
"赵云\n"
]
}
],
"source": [
"def myName():\n",
" print(general_name) # 函数内部访问全局变量general_name,打印其值 赵云\n",
"\n",
"general_name = '赵云' # 定义全局变量general_name\n",
"myName() # 调用函数myName()\n",
"print(general_name) # 函数外部访问全局变量general_name,打印其值 赵云"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"函数内部<font color=Red>__可以__</font>直接访问和引用全局变量的值,但<font color=Red>__不能直接改变__</font>全局变量的值。在函数内部,全局变量应该出现<font color=Red>__在赋值符号的右边__</font>,一旦全局变量出现在赋值符号的<font color=Red>__左边__</font>,系统会认为这是<font color=Red>__重新创建__</font>了一个<font color=Red>__同名的局部变量对象__</font>。其值变为在函数体内重赋的新值,同时<font color=Red>__屏蔽__</font>外层作用域中的同名变量。"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"全局变量的内存地址:140358071021072\n",
"局部变量的内存地址:140358071020688\n",
"局部变量general_name的值:张飞\n",
"全局变量general_name的值:赵云\n"
]
}
],
"source": [
"def myName():\n",
" general_name = '张飞' #函数内定义局部变量,会屏蔽掉同名的全局变量\n",
" print(\"局部变量的内存地址:{}\".format(id(general_name)))\n",
" print(\"局部变量general_name的值:{}\".format(general_name)) # 输出局部变量name的值 张飞 \n",
"\n",
"general_name = '赵云' #函数外定义的全局变量\n",
"print(\"全局变量的内存地址:{}\".format(id(general_name)))\n",
"myName() # 调用函数myName() \n",
"print(\"全局变量general_name的值:{}\".format(general_name)) # 输出全局变量name的值 赵云 "
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"在这个例子里,general_name 本是一个全局变量,myName()函数中被放到了赋值符号左侧重新赋值,这个操作相当于重新创建了一个新的对象“张飞”,并为这个对象贴上一个标签“general_name”。虽然两个对象的名称一样,但分配的内存地址不同,所以是不同的两个对象。因为这个对象和标签都是在函数内部创建的,所以是一个<font color=Red>__新的局部变量__</font>,其作用域是这个函数体。\n",
"\n",
"也就是说,<font color=Red>__在函数体内__</font>访问general_name,<font color=Red>__访问的是局部变量__</font>,其值是“张飞”;<font color=Red>__在函数体外__</font>访问变量general_name时<font color=Red>__访问的是全局变量__</font>,其值是“赵云”。"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"这个规则适用于所有全局变量值为<font color=Red>__固定数据类型__</font>的情况,如全局变量值为数字型、字符型和元组等。此时,函数体内试图改变全局变量值时,都会创建一个新的局部变量。\n",
"\n",
"在函数内部的变量声明,除非<font color=Red>__特别声明为全局变量__</font>,否则均默认为局部变量。\n",
"当需要在函数体内声明一个可以在函数体外访问的全局变量的值时,可以使用<font color=Red>__global关键字__</font>来声明变量的作用域为全局。global的作用就是把局部变量提升为全局变量。"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"原全局变量name的内存地址:140358070583632\n",
"原全局变量name的值:赵云\n",
"新全局变量general_name的内存地址:140358070583536\n",
"新全局变量general_name的值:张飞\n",
"general_name的值:张飞\n",
"general_name的内存地址:140358070583536\n"
]
}
],
"source": [
"def myName():\n",
" global general_name # 声明一个全局变量general_name\n",
" general_name = '张飞' # 为全局变量general_name赋值\n",
" print(\"新全局变量general_name的内存地址:{}\".format(id(general_name)))\n",
" print(\"新全局变量general_name的值:{}\".format(general_name)) #输出局部变量general_name值‘张飞’\n",
"\n",
"\n",
"general_name = '赵云'\n",
"print(\"原全局变量name的内存地址:{}\".format(id(general_name)))\n",
"print(\"原全局变量name的值:{}\".format(general_name)) # 输出全局变量general_name的值‘赵云’\n",
"myName() # 调用函数myName()\n",
"print(\"general_name的值:{}\".format(general_name)) # 输出全局变量general_name的值‘张飞’\n",
"print(\"general_name的内存地址:{}\".format(id(general_name)))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"从输出结果可以发现,在myName()函数调用后,<font color=Red>__函数外再次访问全局变量__</font>general_name时,访问的是最近在函数内部声明的全局变量。"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"<font color=Red>__当全局变量值为列表等可变数据类型__</font>时,如果<font color=Red>__函数内部需要修改全局列表变量的值__</font>时,<font color=Red>__不需要使用global关键字__</font>进行声明,直接可以使用。这是因为列表等可变数据类型的值的修改是在原内存进行的,只有显式声明才会重新创建对象。"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"def myName():\n",
" lsx.append(1) #改变全局变量lsx的值,未重新创建对象\n",
" lsy = [2,3,4] #重新创建了对象,此时lsy为局部变量\n",
" print(id(lsx),id(lsy)) #打印内存地址\n",
" print(lsx,lsy) # lsx 的值为[1] ,lsy 的值为[2, 3, 4]\n",
"\n",
"\n",
"lsx = [] #lsx为全局变量\n",
"lsy = [] #lsy为全局变量\n",
"print(id(lsx),id(lsy)) # 打印内存地址\n",
"myName() # 调用函数myName()\n",
"print(lsx,lsy) # lsx的值变为[1],lsy值仍为 [],未发生变化"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"可以发现,<font color=Red>__函数内外lsx的id值相同__</font>,说明二者是相同的对象,函数内只是修改了lsx并<font color=Red>__未重新创建对象__</font>。\n",
"\n",
"而<font color=Red>__函数内外lsy的id值不同__</font>,说明二者是不同的对象,函数内部lsy = [2,3,4]这条语句<font color=Red>__重新创建对象__</font>[2,3,4],并赋值给变量lsy。"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.7.5"
}
},
"nbformat": 4,
"nbformat_minor": 2
}