Python 学习 第七篇:函数1(定义、调用和变量的作用域)
函数是把一些语句集合在一起的程序结构,用于把复杂的流程细分成不同的组件,能够减少代码的冗余、代码的复用和修改代码的代价。
函数可以0个、1个或多个参数,向函数传递参数,可以控制函数的流程。函数还可以返回代码执行的结果,从技术上讲,任何函数都要返回结果,一个没有返回值的函数会自动返回none对象。如果调用者需要函数返回结果,需要显式使用return语句。
一,函数的定义
Python使用def 语句将创建一个函数对象,并将其赋值给一个变量名,()内是函数的参数,参数通过赋值向参数传值。
def fun_name (arg1,age2...):
fun_body
return语句表示函数调用的结束,并把结果传递给调用者。return语句是可选的,如果它没有出现,那么函数将会在控制流执行完函数主体时结束。
1,def是可执行的语句
def语句是实时执行的,不仅def语句,Python中的所有语句都是实时运行的,并没有独立的编译时间。def语句是一个可执行的语句,在def执行前,函数并不存在,直到def语句执行之后,函数才被创建。
2,函数是一个对象,函数名是变量
def语句是一个赋值语句,函数是一个对象,函数名是一个变量,def语句设置函数名和函数对象的引用。
当def语句运行的时候,它创建了一个新的函数对象,并将其赋值给一个变量名。
3,参数是通过赋值传递的
Python通过赋值(=)把参数传递给函数,这和普通的赋值语句(a=1,或a=b)的行为是相同。当传递常量给参数时,参数引用的是一个全新的对象;当传递变量给参数,参数和变量是对象的共享引用。
4,函数是可以嵌套的
一个def是一个语句,可以出现在任一语句可以出现的地方,甚至嵌套在其他的语句中。这就是说,函数内部可以嵌套函数的定义,例如:
def times(x,y):
z=x*y
def print_result():
print(z)
print_result()
函数 print_result()是一个嵌入函数,定义在函数times(x,y)之内。
实际上,Python的函数是有层次结构,最外层的def是顶层函数,在顶层函数内部定义的函数是嵌套函数。
二,函数的调用
函数通过表达式调用,传入一些值,并返回结果。函数调用的格式是:函数名 + (args),小括号中是传递给参数的变量或值,例如:
func_name(var1,var2...)
如果函数存在返回值,使用变量来接收函数的返回值:
ret_value=fun_name(var1,var2...)
在调用函数时,函数的行为依赖于类型,这是因为函数是语句的集合,语句包含操作符,而操作符的行为是依赖于类型的,
例如,函数times返回两个参数的乘积,当传入数字时,函数 times(2,4) 返回8,* 的作用是计算乘积;当传入字符类型时,函数 times('ab',2) 返回'abab',*号的作用是重复字符串。换句话说,函数times()的作用取决于传递给它的值。
def times(x,y):
return x *y
调用函数时,这种依赖于类型的行为称为多态,就是说,一个操作的作用取决于操作对象的类型。函数的多态性,使得函数可以自动适用于所有类别的对象类型。
如果传给函数的对象有预期的方法和表达式操作符,那么函数就兼容对象。如果传递的对象不支持预期的接口,Python会在 * 表达式运行时检测到错误,并自动抛出一个异常。
这种特性,使得Python代码不应该关心特定的类型,函数应该为对象编写接口,而不是数据类型。当然,这种多态的编程模型意味着:必须测试代码去检测执行结果是否错误,而不是编写代码进行类型检查。
三,变量的作用域
在Python代码中变量无处不在,命名空间就是保存变量名的地方,变量名能够访问(可见)的命名空间叫做作用域。
当在程序中使用变量名时,Python创建、改变或者查找变量都是在命名空间中进行的,变量名被赋值的位置决定了变量名能够被访问的范围。
1,变量的分类
Python中的变量在第一次被赋值时创建,并且必须经过赋值后才能使用。由于没有变量的声明,Python把变量名被赋值的地点关联为(绑定为)一个特定的命名空间。
根据变量的命名空间,把变量大致分为三类:
- 模块是全局命名空间,其名称是模块文件的名称,位于模块内的顶层函数名和变量名叫做全局变量。
- 函数是局部命名空间,其名称是函数的名称,位于函数内的函数名和变量名叫做本地变量。
- 由于一个函数可以嵌套在其他函数内,这使得函数的定义具有层次。我们把一个不在本函数内定义的、而是在上层函数中定义的本地变量叫做非本地变量,也就是,这个变量不是当前函数的本地变量,而是上层函数的本地变量。
2,变量的作用域
变量的作用域是指变量可见的范围,一个变量的作用域总是由变量被赋值的地方决定的,也就是说,变量被赋值的地方决定了变量可见的范围。
- 如果一个变量赋值的地点是在def之内,那么该变量是本地变量,作用域在def之内,在def之外,本地变量是不可见的。
- 如果一个变量赋值的地点是在def之外,那么该变量是全局变量,作用域是全局的,在函数内可以引用全局变量。
在默认情况下,一个函数的所有变量名都是与函数的命名空间相关联的:
- 一个在def内定义的变量名只能被def的代码使用,不能在函数的外部调用该变量名;
- def中的变量名与def之外的变量名并不冲突,一个在def之外被赋值的变量x和在def中被赋值的变量x是不同的变量。
由于变量可以在三个地方分配,那么变量的作用域实际上分为三类:
- 如果一个变量是在def之内赋值,变量可见范围是在函数内,变量的作用域是本地(local);
- 如果一个变量是在def之内赋值,对该函数中嵌套的函数来说,该变量的作用域是非本地的(nonlocal),或称作嵌套(enclosed)作用域;
- 如果一个变量是在def之外赋值,变量可见的范围是整个模块,变量的作用域是全局(global)。
3,作用域法则
所有变量名都可以归纳为内置的(builtin)、全局的(global)和本地的(local)。
内置的模块是Python预先定义好,可以直接引用的。
模块定义的是全局作用域,全局作用域的作用范围仅限于单个模块(文件),也就是说,在一个文件的顶层的变量名对于这个文件内部的代码而言是全局的。
在默认情况下,在函数内部,赋值的变量名除非声明为全局变量或非本地变量之外,都是本地作用域内的。函数还定义了嵌套的作用域,使其内部使用的变量名本地化,以便函数内部使用的变量名不会与函数外的变量名冲突。每次对函数的调用都会创建一个新的本地作用域。如果需要给一个嵌套的def中的变量名赋值,从Python 3.0开始,可以使用 nonlocal语句声明来做到。
注意:模块顶层的函数名是全局变量,函数内部的def定义的是局部变量;函数的参数是本地变量;一个函数内部的任何类型的赋值都会把一个变量划定为本地的,这意味着,函数内部的赋值(=)语句,def语句等,定义的都是本地变量。
4,变量名解析(LEGB原则)
变量名的解析遵从LEGB原则,当引用一个变量时,Python按照以下顺序依次进行查找:从本地变量中、在任意上层函数的作用域、在全局作用域,最后在内置作用域中查找。
LEGB法则解析变量名的详细机制:
- 当在函数中引用变量名时,Python依次搜索4个作用域:本地作用域(L),然后是上一层结构中def的本地作用域(E),再然后是全局作用域(G),最后是内置作用域(B),并且再第一处能够找到该变量名的地方停下来。如果变量名再这次搜索中没有找到,Python会报错。
- 当在函数中给一个变量名赋值时(而不是在一个表达式中对其进行引用),Python总是创建或改变本地作用域的变量名,除非它已经在当前函数中声明为全局变量(global)或者非本地变量(nonlocal)。
把嵌套作用域定义为:在当前的def语句之外,在顶层def语句之内的作用域,嵌套作用域的解析细节:
- 对变量x的引用,首先在当前函数内查找变量名x;之后会向上层的函数中查找变量名x,从内向外依次查找嵌套作用域;之后查找当前的全局作用域(模块);最后再到内置作用域内查找。而全局声明将会直接从全局作用域(模块)进行搜索。
- 对变量x的赋值,如果变量x在函数内部声明为全局变量(global x),那么赋值会修改全局变量x的值;如果变量x在函数内被声明为非本地变量(nonlocal x),那么赋值会修改最近的嵌套函数的本地作用域内的变量x的值。
5,在函数内引用全局变量
global不是声明一个类型,而是声明命名的命名空间是全局的,也就是说,告诉函数打算声明一个或多个全局变量名。
对于全局变量名,这里对用法作一个总结:
- 全局变量是位于模块内部顶层的变量名;
- 如果要在函数内对全局变量进行赋值,那么必须声明该变量是全局的;
- 全局变量名在函数的内部可以直接引用。
例如,x是全局变量,在函数func中使用global 声明x是全局变量,对x赋值,就是修改全局变量的值:
x=11
def func():
global x
x=12
使用global语句把变量声明为全局变量,这样,在函数内部就可以修改全局变量的值,也就是说,global语句允许在def中修改全局变量的值。
global语句包含了关键字global,其后跟着一个或多个由逗号分开的变量名,当在函数内被赋值或引用时,所有列出来的变量都被映射到全局变量名。
global x,y,z
6,在函数内引用上层的非本地变量
Pytho 3.0 引入了nonlocal语句,用于在一个函数内声明一个非本地的变量,该变量定义于一个def语句中,并且位于嵌套作用域的上层。
例如,函数foo1定义了变量var1和var2,要想在函数foo2中改变它们的值,必须在foo2中使用nonlocal语句把它们声明为非本地变量:
def foo1:
var1=1
var2=2
...
def foo2:
nonlocal var1,var2,..
nonlocal语句是一个声明语句,用于把函数内的变量声明为非本地变量。非本地变量是指不在本函数内定义的,而是在上层函数中定义的本地变量。
nonlocal语句的用法解析:
- nonlocal语句完全忽略当前函数的本地作用域,这意味着,nonlocal语句使得对该语句列出的名称的查找从上层函数的作用域开始,而不是从语句声明的本地作用域开始。
- nonlocal语句列出的名称,必须在一个嵌套的def中提前定义过,否则,Python将会产生一个错误,也就是说,nonlocal语句声明的变量只能是def中定义的本地变量,而不能是模块的全局变量。
- nonlocal语句允许对非本地变量赋值,修改其值。
- 在内嵌的函数中,可以直接引用非本地变量,不需要使用nonlocal语句声明。
nonlocal语句提供了一种方式,使得嵌套的函数能够提供可写的状态信息,以便在随后调用嵌套的函数时,能够记住这些信息。简而言之,nonlocal语句的引入使得Python允许修改非本地变量。
四,闭合函数
Python的闭合函数是指一个能够记住嵌套作用域的变量值的函数,尽管那个作用域已经不存在了。
例如,创建一个闭合函数maker,该函数生成并返回一个嵌套函数action,却并调用这个内嵌的函数。
def maker(x):
def action(y):
return x*y
return action
调用闭合函数,得到的是生成的内嵌函数的一个引用。当我们调用闭合函数,它会返回内嵌函数的引用;当调用内嵌函数action时,我们发现尽管闭合函数已经返回并退出,但是,内嵌函数记住了闭合函数内部的变量x的值。
f=maker(2)
f(3)
也就是说,闭合函数的本地作用域的信息被保留了下来。为了能够在内嵌的def中使用变量x的值,Python自动记住了所需要的上层作用域的任意值。
注意:如果lambda或者def在函数中定义,嵌套在一个循环之中,并且嵌套的函数引用了一个上层作用域的变量,该变量被循环所改变,所有在这个循环中产生的函数将会由相同的值——在最后一次循环中完成时被引用变量的值。
>>> def maker():
... acts=[]
... for i in range(5):
... acts.append(lambda x:i**x)
... return acts
...
>>> acts=maker()
>>> acts[0](2)
16
因为嵌套作用域中的变量在嵌套的函数被调用时才进行检查,所以,它们实际上记住的是同样的值(在最后一次循环迭代中循环变量的值)。
也就是说,只有当调用acts[0](2)时,采取检查变量i的值,此时变i的值是最后一次迭代的值4。要解决这类问题,必须在函数maker调用时,对i的值进行评估,并保存起来。
>>> def maker():
... acts=[]
... for i in range(5):
... acts.append(lambda x, i=i : i**x)
... return acts
为了让这类代码能够工作,必须使用默认参数把当前的值传递给嵌套作用域的变量。因为默认参数是在嵌套函数创建时评估的(而不是其稍后调用时),所以,每一个函数都记住了自己的变量 i 的值。
参考文档:
Python 学习 第七篇:函数1(定义、调用和变量的作用域)的更多相关文章
- python - 函数的相互调用 及 变量的作用域
# -*- coding:utf-8 -*- '''@project: jiaxy@author: Jimmy@file: study_函数的相互调用及变量的作用域.py@ide: PyCharm C ...
- python学习二十三天函数的定义
在计算机编程中,函数就是可以重复调用,可以传递参数,减少代码的量,可以高效写出好的代码,提高软件的运行质量,下面简单讲述python函数的定义方式 1,函数的定义 函数的定义用关键词def 函数名跟 ...
- Python学习第十篇——函数初步
def make_album(name,album_name,song_nums = 1): dict_album = {name:[album_name]} if int(song_nums) &g ...
- python学习第二十九天函数局部变量如何改变外部变量
python函数局部变量如何改变外部变量,之前我说过,局部变量是没办法改变外部变量的,除非局部变量找不到,去外部找,输出变量,使用关键词global 使变量改变外部变量. 1,使用关键词global ...
- python学习第七篇——字典访问键与值
此程序的目的在于,正确而简单的访问字典的键与值 favorite_languages={ 'jen':['python','c'], 'sarah':['c'], 'edward':['ruby',' ...
- Python学习第十三篇——函数的深层次运用
def make_pizza(size,*toppings): print("\nmaking a "+str(size)+" size pizza with follo ...
- Python学习笔记基础篇——总览
Python初识与简介[开篇] Python学习笔记——基础篇[第一周]——变量与赋值.用户交互.条件判断.循环控制.数据类型.文本操作 Python学习笔记——基础篇[第二周]——解释器.字符串.列 ...
- python学习第七讲,python中的数据类型,列表,元祖,字典,之元祖使用与介绍
目录 python学习第七讲,python中的数据类型,列表,元祖,字典,之元祖使用与介绍 一丶元祖 1.元祖简介 2.元祖变量的定义 3.元祖变量的常用操作. 4.元祖的遍历 5.元祖的应用场景 p ...
- Go语言学习笔记七: 函数
Go语言学习笔记七: 函数 Go语言有函数还有方法,神奇不.这有点像python了. 函数定义 func function_name( [parameter list] ) [return_types ...
随机推荐
- UML类图关系图解
一.类结构 在类的UML图中,使用长方形描述一个类的主要构成,长方形垂直地分为三层,以此放置类的名称.属性和方法. 其中, 一般类的类名用正常字体粗体表示,如上图:抽象类名用斜体字粗体,如User:接 ...
- python第七十六天--堡垒机完成
堡垒机windows ,linux 都通过测试初始化说明: #进入根目录 1.初始化表结构 #python3 bin/start.py syncdb 2.创建堡垒机用户 #python3 bin/st ...
- Django之model模块创建表完整过程
Django中,与数据库相关的模块是model模块,它提供了一种简单易操作的API方式与数据库交互,它是通过ORM映射的方式来操作数据库,一个类对应数据库一张表,一个类属性,对应该表的一个字段,一个实 ...
- IE push方法,最后一个参数后面不能跟",",否则报语法错误
var columns = [[]]; columns[0].push( { field: 'ADDNAME', title: '添加人', width: 80, }, { field: 'ADDDT ...
- linux内存源码分析 - SLAB分配器概述【转】
本文为原创,转载请注明:http://www.cnblogs.com/tolimit/ 之前说了管理区页框分配器,这里我们简称为页框分配器,在页框分配器中主要是管理物理内存,将物理内存的页框分配给申请 ...
- Head First Android --- Enable USB debugging on your device
1. Enable USB debugging on your device On your device, open “Developer options” (in Android 4.0 o ...
- 【PAT】B1037 在霍格沃茨找零钱(20 分)
#include<stdio.h> #include<string.h> #include<algorithm> using namespace std; int ...
- vSphere ESXi 重新安装后的虚拟机恢复(转载)
安装的 ESXi 的物理主机密码忘记,登录 不上了,需要重新安装 ESXi,安装后恢复原先物理主机上的 虚拟机的方法如下(VMFS分区完好): 关于 VMFS 分区: ESXi 的安装时会划分一个分区 ...
- VRS待解决的问题——原因及解决方案
1.持续滤波失败(查看文档) 通过查看文档及代码 2.GAL卫星数为0的网元及原因 3.判断发的是否是单个基站(网元未固定),多个用户进行测试 4.网元固定率(采用文件输出) 5.是否频繁重复初始化 ...
- 【HTTP缓存】浏览器缓存理论知识
时间:2016-12-12 17:51:30 作者: zhongxia 零.前言 这里主要写的是理论,具体实践的比较少,后期写一个实践教程,内容基本都是从参考文章里面抄过来的[看完文章,顺便写做下笔记 ...