master
/ 4.3 变量的作用域.ipynb

4.3 变量的作用域.ipynb @master

341084b
 
 
 
 
 
 
 
 
 
 
 
 
d487d71
341084b
 
 
 
 
 
d487d71
341084b
d487d71
341084b
d487d71
341084b
 
 
 
 
 
 
 
 
 
d487d71
341084b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
d487d71
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
341084b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
d487d71
341084b
d487d71
 
 
 
 
 
 
 
 
 
341084b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
d487d71
341084b
d487d71
 
 
 
 
 
 
 
 
 
 
 
341084b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
d487d71
341084b
d487d71
 
 
 
 
 
 
 
 
 
 
 
 
 
341084b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
d487d71
341084b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
{
 "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
}