您的位置:首页 > 编程语言 > Python开发

Python中的函数

2015-08-03 09:27 579 查看
http://www.cnblogs.com/dolphin0520/archive/2013/03/18/2966674.html

一.函数的定义

  在某些编程语言当中,函数声明和函数定义是区分开的(在这些编程语言当中函数声明和函数定义可以出现在不同的文件中,比如C语言),但是在Python中,函数声明和函数定义是视为一体的。在Python中,函数定义的基本形式如下:

def function(params):
block
return expression/value


  在这里说明几点:

  (1)在Python中采用def关键字进行函数的定义,不用指定返回值的类型。

  (2)函数参数params可以是零个、一个或者多个,同样的,函数参数也不用指定参数类型,因为在Python中变量都是弱类型的,Python会自动根据值来维护其类型。

  (3)return语句是可选的,它可以在函数体内任何地方出现,表示函数调用执行到此结束;如果没有return语句,会自动返回NONE,如果有return语句,但是return后面没有接表达式或者值的话也是返回NONE。下面看两个例子:

def printHello():
print 'hello'

def printNum():
for i in range(0,10):
print i
return

def add(a,b):
return a+b

print printHello()
print printNum()
print add(1,2)


 二.函数的使用

  在定义了函数之后,就可以使用该函数了,但是在Python中要注意一个问题,就是在Python中不允许前向引用,即在函数定义之前,不允许调用该函数。看个例子就明白了:

print add(1,2)

def add(a,b):
return a+b


  这段程序运行的结果是:



  从报的错可以知道,名字为"add"的函数未进行定义。所以在任何时候调用某个函数,必须确保其定义在调用之前。

在学习C语言函数的时候,遇到的问题主要有形参实参的区别、参数的传递和改变、变量的作用域。同样在Python中,关于对函数的理解和使用也存在这些问题。下面来逐一讲解。
一.函数形参和实参的区别

  相信有过编程语言经验的朋友对形参和实参这两个东西并不陌生。形参全称是形式参数,在用def关键字定义函数时函数名后面括号里的变量称作为形式参数。实参全称为实际参数,在调用函数时提供的值或者变量称作为实际参数。举个例子:

#这里的a和b就是形参
def add(a,b):
return a+b

#这里的1和2是实参
add(1,2)
x=2
y=3
#这里的x和y是实参
add(x,y)


二.参数的传递和改变

  在大多数高级语言当中,对参数的传递方式这个问题的理解一直是个难点和重点,因为它理解起来并不是那么直观明了,但是不理解的话在编写程序的时候又极其容易出错。下面我们来探讨一下Python中的函数参数的传递问题。

  首先在讨论这个问题之前,我们需要明确一点就是在Python中一切皆对象,变量中存放的是对象的引用。这个确实有点难以理解,“一切皆对象”?对,在Python中确实是这样,包括我们之前经常用到的字符串常量,整型常量都是对象。不信的话可以验证一下:

print id(5)
print id('python')
x=2
print id(x)
y='hello'
print id(y)


  这段代码的运行结果是:

  


  先解释一下函数id( )的作用。下面这段话是官方文档对id()函数的解释:

  


  显而易见,id(object)函数是返回对象object在其生命周期内位于内存中的地址,id函数的参数类型是一个对象,因此对于这个语句
id(2)没有报错,就可以知道2在这里是一个对象。下面再看几个例子:

x=2
print id(2)
print id(x)
y='hello'
print id('hello')
print id(y)


  其运行结果为:

  


  从结果可以看出,id(x)和id(2)的值是一样的,id(y)和id('hello')的值也是一样的。
  在Python中一切皆对象,像2,'hello'这样的值都是对象,只不过5是一个整型对象,而'hello'是一个字符串对象。上面的x=2,在Python中实际的处理过程是这样的:先申请一段内存分配给一个整型对象来存储整型值2,然后让变量x去指向这个对象,实际上就是指向这段内存(这里有点和C语言中的指针类似)。而id(2)和id(x)的结果一样,说明id函数在作用于变量时,其返回的是变量指向的对象的地址。因为变量也是对象,所以在这里可以将x看成是对象2的一个引用。
  下面再看几个例子:

x=2
print id(x)
y=2
print id(y)
s='hello'
print id(s)
t=s
print id(t)


  其运行结果为:

  


  从运行结果可以看到id(x)和id(y)的结果是相同的,id(s)和id(t)的结果也是相同的。这说明x和y指向的是同一对象,而t和s也是指向的同一对象。x=2这句让变量x指向了int类型的对象2,而y=2这句执行时,并不重新为2分配空间,而是让y直接指向了已经存在的int类型的对象2.这个很好理解,因为本身只是想给y赋一个值2,而在内存中已经存在了这样一个int类型对象2,所以就直接让y指向了已经存在的对象。这样一来不仅能达到目的,还能节约内存空间。t=s这句变量互相赋值,也相当于是让t指向了已经存在的字符串类型的对象'hello'(这个原理和C语言中指针的互相赋值有点类似)。
  看这幅图就理解了:
  


  下面再看几个例子:

x=2
print id(2)
print id(x)
x=3
print id(3)
print id(x)
L=[1,2,3]
M=L
print id(L)
print id(M)
print id(L[2])
L[0]=2
print id(L)
print M


  运行结果为:

  


  下面来分析一下这个结果,两次的id(x)的值不同,这个可能让人有点难以理解。注意,在Python中,单一元素的对象是不允许更改的,比如整型数据、字符串、浮点数等。x=3这句的执行过程并不是先获取x原来指向的对象的地址,再把内存中的值更改为3,而是新申请一段内存来存储对象3,再让x去指向对象3,所以两次id(x)的值不同。然而为何改变了L中的某个子元素的值后,id(L)的值没有发生改变?在Python中,复杂元素的对象是允许更改的,比如列表、字典、元组等。Python中变量存储的是对象的引用,对于列表,其id()值返回的是列表第一个子元素L[0]的存储地址。就像上面的例子,L=[1,2,3],这里的L有三个子元素L[0],L[1],L[2],L[0]、L[1]、L[2]分别指向对象1、2、3,id(L)值和对象3的存储地址相同,看下面这个图就明白了:
  


  因为L和M指向的是同一对象,所以在更改了L中子元素的值后,M也相应改变了,但是id(L)值并没有改变,因为这句L[0]=2只是让L[0]重新指向了对象2,而L[0]本身的存储地址并没有发生改变,所以id(L)的值没有改变( id(L)的值实际等于L[0]本身的存储地址)。

  下面就来讨论一下函数的参数传递和改变这个问题。
  在Python中参数传递采用的是值传递,这个和C语言有点类似。先看几个例子:

def modify1(m,K):
m=2
K=[4,5,6]
return

def modify2(m,K):
m=2
K[0]=0
return

n=100
L=[1,2,3]
modify1(n,L)
print n
print L
modify2(n,L)
print n
print L


  程序运行结果为:

  


  从结果可以看出,执行modify1( )之后,n和L都没有发生任何改变;执行modify2( )后,n还是没有改变,L发生了改变。因为在Python中参数传递采用的是值传递方式,在执行函数modify1时,先获取n和L的id( )值,然后为形参m和K分配空间,让m和K分别指向对象100和对象[1,2,3]。m=2这句让m重新指向对象2,而K=[4,5,6]这句让K重新指向对象[4,5,6]。这种改变并不会影响到实参n和L,所以在执行modify1之后,n和L没有发生任何改变;在执行函数modify2时,同理,让m和K分别指向对象2和对象[1,2,3],然而K[0]=0让K[0]重新指向了对象0(注意这里K和L指向的是同一段内存),所以对K指向的内存数据进行的任何改变也会影响到L,因此在执行modify2后,L发生了改变。

三.变量的作用域

  在Python中,也存在作用域这个问题。在Python中,会为每个层次生成一个符号表,里层能调用外层中的变量,而外层不能调用里层中的变量,并且当外层和里层有同名变量时,外层变量会被里层变量屏蔽掉。举个例子:

def function():
x=2
count=2
while count>0:
x=3
print x
count=count-1

function()


  在函数function中,while循环外面和while循环里面都有变量x,此时,while循环外面的变量x会被屏蔽掉。注意在函数内部定义的变量作用域都仅限于函数内部,在函数外部是不能够调用的,一般称这种变量为局部变量。

  还有一种变量叫做全局变量,它是在函数外部定义的,作用域是整个文件。全局变量可以直接在函数里面应用,但是如果要在函数内部改变全局变量,必须使用global关键字进行声明。

x=2

def fun1():
print x

def fun2():
global x
x=3
print x

fun1()
fun2()
print x


  关于函数的形参和实参、参数的传递以及变量的作用域问题这里就讨论这么多了,下面一起来了解一下函数参数类型的问题。在C语言中,调用函数时必须依照函数定义时的参数个数以及类型来传递参数,否则将会发生错误,这个是严格进行规定的。然而在Python中函数参数定义和传递的方式相比而言就灵活多了。
一.函数参数的类型

  之前我们接触到的那种函数参数定义和传递方式叫做位置参数,即参数是通过位置进行匹配的,从左到右,依次进行匹配,这个对参数的位置和个数都有严格的要求。而在Python中还有一种是通过参数名字来匹配的,这样一来,不需要严格按照参数定义时的位置来传递参数,这种参数叫做关键字参数。下面举两个例子:

def display(a,b):
print a
print b

display('hello','world')


  这段程序是想输出'hello world',可以正常运行。如果像下面这样写的话,结果可能就不是预期的样子了:

def display(a,b):
print a
print b

#这样会报错
display('hello')

#这样会输出'world hello'
display('world','hello')


  可以看出在Python中默认的是采用位置参数来传参。这样调用函数必须严格按照函数定义时的参数个数和位置来传参,否则将会出现预想不到的结果。下面这段代码采用的就是关键字参数:

def display(a,b):
print a
print b

#下面2句达到的效果是相同的
display(a='world',b='hello')
display(b='hello',a='world')


  可以看到通过指定参数名字传递参数的时候,参数位置对结果是没有影响的。

  关键字参数最厉害的地方在于它能够给函数参数提供默认值。比如:

def display(a='hello',b='wolrd'):
print a+b

display()
display(b='world')
display(a='hello')
display('world')


  在上面的代码中,分别给a和b指定了默认参数,即如果不给a或者b传递参数时,它们就分别采用默认值。在给参数指定了默认值后,如果传参时不指定参数名,则会从左到右依次进行传参,比如display('world')没有指定'world'是传递给a还是b,则默认从左向右匹配,即传递给a。

  使用默认参数固然方便,但是有一点要非常注意,在重复调用函数时默认形参会继承之前一次调用结束之后该形参的值。下面看一个例子:

def insert(a,L=[]):
L.append(a)
print L

insert('hello')
insert('world')


  其运行结果为:

  


二.任意个数参数

  一般情况下我们在定义函数时,函数参数的个数是确定了的,然而某些情况下我们是不能确定参数的个数的,比如要存储某个人的名字和它的小名,某些人小名可能有2个或者更多个,此时无法确定参数的个数,就可以使用收集参数了,使用收集参数只需在参数前面加上'*'或者'**'。

def storename(name,*nickName):
print 'real name is %s' %name
for nickname in nickName:
print nickname

storename('jack')
storename(u'詹姆斯',u'小皇帝')
storename(u'奥尼尔',u'大鲨鱼',u'三不沾')


  


  '*'和'**'表示能够接受0到任意多个参数,'*'表示将没有匹配的值都放在同一个元组中,'**'表示将没有匹配的值都放在一个dictionary中。

def printvalue(a,**d):
print 'a=%d' %a
for x in d:
print x+'=%d' %d[x]

printvalue(1,b=2,c=3)


  


  最后需要补充一点:Python中函数是可以返回多个值的,如果返回多个值,会将多个值放在一个元组或者其他类型的集合中来返回。

def function():
x=2
y=[3,4]
return x,y

print function()


  


  关于函数的东西暂时就只讲这么多了,具体的用法和细节需要在平时写代码中慢慢理解和积累。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: