python渐进-函数基本篇

函数、模块、类,是组织代码的良好工具。实现某个功能的代码块可以通过函数集合在一起;拥有相似功能的代码块可以通过模块集中在某个目录某个文件里面;封装了某个面向对象的代码可以通过类来打包。

有了这些代码组织工具,大型的软件工程和项目才能够条理清晰地被管理起来。这里就先从函数说起。

11.1 函数的定义

使用def语句可以定义一个函数。一个函数一般会有函数名、函数参数,函数体和函数返回值。

下面举一个简单的例子,一个实现加1的功能的函数,对传递进来的参数实现+1,并进行返回。代码如下

def inc(a): return a+1

其中inc是函数的名字,

a是函数的参数,

函数体是return a+1,

而返回值是 a+1。

使用函数名和参数,就可以调用函数,接收函数的返回值了。演示代码如下:

ainc=inc(4)print(ainc)

其中inc是刚刚定义的函数,

4是函数的调用参数,

ainc是接收函数返回值的变量。

运行上述的代码,返回是

5

定义函数的时候是可以没有返回值的,也就是没有return语句。实际上python还是会默认返回一个None的。

下面的例子是一个没有return的函数。

def inclist(l): for i in range(len(l)): l[i]=l[i]+1

这个函数是对列表中的所有元素都加1,调用的演示代码为:

l=[1,2,3,4]inclist(l)print(l)

运行上述代码返回为

[2, 3, 4, 5]

11.2 函数里的变量的定义域

定义一个函数,就开辟了一个新的命名空间,拥有了自己的符号表。在函数里面对一个变量进行赋值操作,这个变量就会被添加到函数的本地符号表里面。此时就会无法使用函数外面的同名变量。

举个例子,在infunc函数里面对变量v和列表l进行了赋值。演示代码为:

def infunc(): v=4 l=[] print('inside func v='+str(v)) print('inside func l='+str(l))

而在全局代码块中也有相同的变量名v和列表l存在。演示代码为:

v=6l=[3,4]infunc()print('ouside funcv='+str(v))print('ouside funcl='+str(l))

运行一下这个代码,看看infunc()中v和l,以及全局代码块的v和l是否对等。

inside func v=4inside func l=[]ouside func v=6ouside func l=[3, 4]

可以看到函数里的变量只在函数里起作用。全局代码块中的同名变量丝毫不受影响。

如果想要在函数内读写全局代码块的变量,需要对变量进行global声明。方法是在函数里面加入声明的语句global v,l

def infunc(): global v,l v=4 l=[] print('inside func v='+str(v)) print('inside func l='+str(l))

此时再运行,结果为:

inside func v=4inside func l=[]ouside func v=4ouside func l=[]

可以看到全局代码块的变量已经可以在函数里进行读写了。

需要注意的是,如果只是在函数里读取全局代码块的变量,并不进行赋值的话,那么函数里依然是可以访问到全局代码块的变量的。以下的代码中,v和l都是在外部定义的,而函数内只是进行了读取。

v=4l=[2,3]def func2(): print(v) print(l)func2()

这段代码的运行结果是

4[2, 3]

之所以产生这样让人困惑的问题,是因为python在定义变量和查找变量的逻辑上存在分歧。

在定义局部变量的时候,python并不会顺藤摸瓜地往上查找。当一个赋值语句到来,python只查找了本地符号表,如果没有则添加一个新变量。

而在查找变量的时候,则复杂得多,python先在本地符号表中查找,在到外围符号表中查找(如果有的话),然后是全局符号表,最后是内置符号表(buildin)。

这两个过程并不对称,所以才产生了这个问题。如果两个函数都需要共享同名变量,都需要对此同名变量进行读写,那么最好把这个同名变量设置为全局的变量。在两个函数当中均使用global进行声明。

11.3 函数的参数

python的函数在定义和调用的时候都可以使用多样化的参数形式。

函数在定义的时候可以使用形式参数,默认参数,*参数(可变列表参数),**参数(可变字典参数)。

形式参数是在函数调用的时候必选的参数。并且调用的时候,实际参数要和定义的形式参数位置和数目保持一致。比如之前的inc(a)函数,a就是形式参数;调用的时候inc(5)中,5就是实际参数。调用inc的时候,必须保证有且只有一个参数,才不会出错。

默认参数也叫做可选参数,默认参数都是key=value格式的,在调用的时候可以带上也可以不带上。函数在定义的时候会给这些参数一个初试值,如果在调用的时候没有带上这些参数,那么函数会使用内存中的当前值。演示代码如下:

def opfunc(oppara=5): print(oppara)opfunc()opfunc(9)

以上的代码定义了一个默认参数名字为oppara,如果函数调用的时候没有带上参数,那么就会使用当前oppara的值。以上的代码演示了带默认参数和不带默认参数的效果,结果为

59

如果有形式参数,默认参数需要跟在必选的参数后面。

需要注意的是,默认参数的默认值只会在函数定义的时候初始化一次,不会在每次函数调用的时候再进行初始化了。因此对于可变变量来说,会受到前几次调用的影响。先看一段代码

def apl(a,L=[]): L.append(a) print(L)apl(1)apl(2)

以上的代码中,apl有一个默认参数L,在定义函数的时候初始化为一个空列表。在之后的两次调用中,L并不会再次被初始化,因此每一次的结果都会被保留下来了。所以这段代码的运行结果为

[1][1, 2]

而对于不可变的变量,就不会存在这种问题。看下面的演示代码:

def m(a=5):

print("a's location:"+str(id(a)))

a=a+1

print("a's location after add:"+str(id(a)))

print(“a's value ”+str(a))

m()

m()

a同样在函数定义的时候进行初始化,并且进行了赋值操作。在代码中把a的存储地址给打印出来看看。运行的结果为:

可以看到以上的运行结果中a的默认值每次都回到了5。这里并不是因为a每次都重新被初始化了。而是a每次+1,a的地址实际上变成了另外一个。而函数每次都只记得默认参数的初始化地址,每次都从初始化的地址取默认值,所以看起来好像a又被初始化了一次一样。

默认参数的初始化,对于可变变量和不可变变量的表现是不一样的,需要注意。

*参数表示函数接受一个可变数量的参数列表。

def listfunc(*args): filepath='' for arg in args: filepath=filepath+'/'+arg print(filepath)

上述的函数通过遍历参数列表,把不同的目录节点连接起来,生成一个文件路径。调用代码为:

listfunc('documents','python','function')

结果为:

/documents/python/function

**表示函数接受一个参数字典。

def dictfunc(**keywords): for k in keywords: print(k+':'+keywords[k])

以上的代码接受一个参数字典,并且把参数字典的key和value打印出来。它可以通过关键字参数的方式进行调用(关键字调用方式在下面介绍)

dictfunc(zhang='98',lee='88',yu='90')

代码运行的结果为

yu:90lee:88zhang:98

以上的内容都是关于函数定义的。函数调用也有一套逻辑,它和以上函数定义的内容有交叉关系,并不是说一种定义方式只能用对应的调用方式来调用。

函数的调用方式有实参调用,关键字调用,*参数调用,**参数调用

实参调用适用于函数定义中有形式参数,默认参数或者*参数列表的函数。

下面的函数定义包括了形式参数,默认参数和*参数。并且在调用的时候使用了实参调用,所有的参数都进行了展开。

def fmfunc(formal,optional='',*args): print(formal) filepath= optional for arg in args: filepath=filepath+'/'+arg print(filepath) fmfunc('windows','c:','documents','python','function')

以上代码的运行结果为

windowsc:/documents/python/function

这个函数也适合用*调用,*调用的参数是一个列表,调用的时候使用*操作把参数展开。修改函数调用代码为

paralist=['windows','c:','documents','python','function']fmfunc(*paralist)

运行结果和实参调用是一致的。

关键字调用适用于函数定义时有形式参数,默认参数或者**参数字典的函数。

def kwfunc(formal,optional='class one',**keywords): print(formal) print(optional) for k in keywords: print(k+':'+keywords[k]) kwfunc(formal='gradeone',optional='class two',zhang='98',lee='88',yu='90')

在函数定义的时候,形式参数放在前面,默认参数放在后面,而参数字典因为长度不定,放在最后。参数字典里面的key值,不能和形式参数以及默认参数一样。否则会出现紊乱。

以上的代码运行结果为

grade oneclass twoyu:90lee:88zhang:98

适合关键字调用的函数,同样也适合**调用。**调用的调用参数是一个字典,调用的时候使用**操作把字典展开。修改函数调用代码

dictpara={'formal':'gradeone','optional':'class two','zhang':'98','lee':'88','yu':'90'}kwfunc(**dictpara)

运行结果也和关键字调用的一样。

每次很长,浏览器就爱崩溃。赶紧保存发掉好了。

  • 发表于:
  • 原文链接https://kuaibao.qq.com/s/20180619G1XT9U00?refer=cp_1026
  • 腾讯「云+社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。

扫码关注云+社区

领取腾讯云代金券