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 ...
随机推荐
- c# 百度地图api APP SN校验失败
在使用c#调用百度地图Web服务api遇到的签名(sn校验)问题,在此记录一下,(ip白名单校验的请忽略) 1.首先获取ak与sk,这个两个东西可以从控制台中获取到 2.在这个地址:sn签名算法,里面 ...
- python3 下列表与字典转换
在写爬虫的时候,经常需要处理cookie,requests库里的cookie是dict,但是headers['cookie']却是一个key=value的字符串. 下面是几个用推导式实现的转换函数,供 ...
- Python3.5中安装Scrapy包时出现问题
在Python3.5中安装Scrapy第三方库 pip install Scrapy 安装到后面出现的这类错误: error: Microsoft Visual C++ 14.0 is require ...
- 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 ...
- django中admin
我们在models中建立了表结构,想要在admin中表示: from django.contrib import admin from . import models for table in mod ...
- 获取请求Url
/// <summary> /// 获取请求Url /// 例:http://www.text:1234 /// </summary> /// <returns>& ...
- Hadoop优化 第一篇 : HDFS/MapReduce
比较惭愧,博客很久(半年)没更新了.最近也自己搭了个博客,wordpress玩的还不是很熟,感兴趣的朋友可以多多交流哈!地址是:http://www.leocook.org/ 另外,我建了个QQ群:3 ...
- 五、git创建及合并分支
1. 创建并切换到dev分支 git checkout -b dev // git checkout命令加上-b参数表示创建并切换,相当于以下两条命令 git branch dev git check ...
- Axure RP Pro7.0的key注册码加汉化非破解
上次我们刚分享过Axure RP Pro6.5 key注册码加汉化非破解,我还要分享一个Axure RP Pro7.0的key注册码加汉化,非破解哦. 当然方法还是不变,先用下面的密钥激活.用户名就是 ...
- NSIS学习记录の----查找注册表某个键是否存在
最近要做一个注册表的判断.以往都是注册表某个键的键值存在查找,但是如何判断一个空键值的键是否存在呢(很多大厂装逼不写键值,有默认就好)? 下面给出解决办法(要沟通请邮件联系:7-7-2-7-0-6-5 ...