函数
(一)定义函数
你可以定义一个由自己想要功能的函数,以下是简单的规则:
(1)函数代码块以 def 关键词开头,后接函数标识符名称和圆括号()。
(2)任何传入参数和自变量必须放在圆括号中间。圆括号之间可以用于定义参数。
(3)函数的第一行语句可以选择性地使用文档字符串—用于存放函数说明。
(4)函数内容以冒号起始,并且缩进。
(5)return结束函数,选择性地返回一个值给调用方。不带表达式的return相当于返回 None。
语法:
def functionname( parameters ):
"函数_文档字符串"
function_suite
return [expression]
默认情况下,参数值和参数名称是按函数声明中定义的的顺序匹配起来的。
练习1:没有返回return的函数,返回none
>>> def sum(a,b):
... print a+b
>>> print sum(5,4)
练习2:返回列表及元组
>>> def bar():
... return 'abc',[42,'python'],'guido'
>>> def foo():
... return ["hello",1000,-98.6]
注:bar()函数返回一个元组,foo()函数返回一个列表,由于元组语法上不一定带上圆括号,所以让人觉得返回了多个值。所以可以适当加上括号。
从返回值的角度看可以通过以下三种方式进行数据的存储:
(在对x,y,z 和a,b,c的赋值中每个变量会接收对应的返回值,而tuple1会隐式地返回整个元组)
>>>tuple1=bar()
>>> x,y,z=bar()
>>> (a,b,c)=bar()
(二)调用函数
定义一个函数只给了函数一个名称,指定了函数里包含的参数,和代码块结构。这个函数的基本结构完成以后,你可以通过另一个函数调用执行,也可以直接从Python提示符执行。
>>> def printme(str):
... print str;
... Return
>>> printme("调用自定义的函数")
练习3:求列表中第二大的数
方法一:
>>> a=[10,50,-1,3,2,10]
>>> def secNum(lista):
... lista.sort()
... return lista[-2]
...
>>> print secNum(a)
10
方法二:
>>> def secNum(lista):
... max_number=max(lista)
... lista.remove(max_number)
... return max(lista)
方法三:
算法:
1 比较列表中前2个数,大的作为为列表中暂时的最大数,小的
作为次大数,分别存在2个变量里
2 遍历列表从第三个数开始以后后的值,去和最大值去比较,存在2种可能
3 如果大于,当前值作为列表中暂时的最大数,以前最大的变为
次大的。
4 如果小于,和次大的比较,大于它就是次大值,小于继续遍历
后续值。
#coding=utf-8
a=[10,10,10,0,10,10,0]
def secondBiggestNumber(lista):
#判断是否是列表或元组
if not isinstance(lista,(list,tuple)):
return None
#判断是否有2个以上元素
if len(lista)<=1:
return None
#判断每个元素的的值是否为数字
for i in lista:
if isinstance(i,(int,float)):
continue
else:
return None
#比较列表中前2个数,大的作为为列表中暂时的最大数,小的
#作为次大数,分别存在2个变量里
if lista[0]>lista[1]:
biggest_number=lista[0]
second_biggest_number=lista[1]
else:
biggest_number=lista[1]
second_biggest_number=lista[0]
#遍历列表从第三个数开始以后后的值,去和最大值去比较
for i in lista[2:]:
#如果大于,当前值作为列表中暂时的最大数,以前最大的变为
#次大的。
if i > biggest_number:
second_biggest_number=biggest_number
biggest_number=i
#如果小于,和次大的比较,大于它就是次大值,小于继续遍历
#后续值。
elif i == biggest_number:
continue
else:
if biggest_number==second_biggest_number:
second_biggest_number=i
if i >second_biggest_number:
second_biggest_number=i
return second_biggest_number
print secondBiggestNumber(a)
练习4:计算10000次浮点数运算的耗时
import time
import random
>>> def compute_float_time_consume():
... start_time=time.time()
... for i in range(10000):
... random.random()**2
... end_time=time.time()
... return end_time-start_time
(三)参数传递
在Python中,类型属于对象,变量是没有类型的:
a=[1,2,3]
a="Runoob"
以上代码中,[1,2,3] 是 List 类型,”Runoob” 是 String 类型,而变量 a 是没有类型,她仅仅是一个对象的引用(一个指针),可以是 List 类型对象,也可以指向 String 类型对象。
(1)可更改(mutable)与不可更改(immutable)对象
在 python 中,strings, tuples, 和 numbers 是不可更改的对象,而 list,dict 等则是可以修改的对象。
不可变类型:变量赋值 a=5 后再赋值 a=10,这里实际是新生成一个 int 值对象 10,再让 a 指向它,而5被丢弃,不是改变a的值,相当于新生成了a。
可变类型:变量赋值 la=[1,2,3,4] 后再赋值 la[2]=5 则是将 list la 的第三个元素值更改,本身la没有动,只是其内部的一部分值被修改了。
(2)python 函数的参数传递:
不可变类型:类似 c++ 的值传递,如 整数、字符串、元组。如fun(a),传递的只是a的值,没有影响a对象本身。比如在 fun(a)内部修改 a 的值,只是修改另一个复制的对象,不会影响a本身。
可变类型:类似 c++ 的引用传递,如列表,字典。如 fun(a),则是将 a 真正的传过去,修改后fun外部的a也会受影响
python 中一切都是对象,严格意义我们不能说值传递还是引用传递,我们应该说传不可变对象和传可变对象。
练习3:传不可变对象
>>> def ChangeInt(a):
... a=10
>>> b=2
>>> ChangeInt(b)
>>> print b #结果是2
练习4:传可变对象
>>> def changeme(mylist):
... mylist.append([1,2,3,4])
... print mylist
... return
>>> mylist=[10,20,30]
>>> changeme(mylist) #[10, 20, 30, [1, 2, 3, 4]]
>>> print mylist #[10, 20, 30, [1, 2, 3, 4]]
注:实例中传入函数的和在末尾添加新内容的对象用的是同一个引用
(2)参数类型
python中函数传递参数有四种形式:
fun1(a,b,c)
fun1(a,b,c)是直接将实参赋予行参,根据位置做匹配,即严格要求实参的数量与行参的数量位置相等,比较一般,大多数语言常用这种方式。
>>> def a(x,y):print x,y
>>> a(4,5) #4 5
fun2(a=1,b=2,c=3)
fun2(a=1,b=2,c=3)根据键值对的形式做实参与行参的匹配,通过这种式就可以忽略了参数的位置关系,直接根据关键字来进行赋值,同时该种传参方式还有个好处就是可以在调用函数的时候作为个别选填项,不要求数量上的相等,即可以fun5(3,4)来调用fun2函数,这里关键就是前面的3,4覆盖了原来a、b两个行参的值,但c还是不变采用原来的默认值3,这种模式相较第一种更加灵活,不仅可以通过fun6(c=5,a=2,b=7)来打乱行参的位置,而且可以在但没有对应行参传递的时候常用定义函数时的默认值。
>>> def a(x,y=3):print x,y
>>> a(4) #4 3
注:Python传可变对象-传默认参数会记住值-这是坑须知道
#coding=utf-8
def add_end(L=[]): #或者L=[1, 2, 3]
L.append('END')
return L
print add_end([1, 2, 3])
print add_end(['x', 'y', 'z'])
print add_end()
print add_end()
print add_end()
只有在默认参数时候,L才会记住每次append的值
如果你每次都传入值,L就会每次显示你传入的数据而不会记住你上一次传入的值
改成如下:
def add_end(L=None):
if L is None:
L=[]
L.append('END')
return L
解析:
Python传默认参数需注意:
默认参数是[],但是函数似乎每次都”记住了”上次添加了’END’后的list;
Python函数在定义的时候,默认参数的值就被计算出来了即[];
因为默认参数L也是一个变量,它指向对象[],每次调用该函数,如果改变了L的内容,则下次调用时,默认参数的内容就变了,不在是函数定义时的[]了;
定义默认参数必须牢记一点:默认参数必须指向不变对象!
所以:函数的默认参数最好别用可变对象,否则存在累加操作结果
的情况
fun3(*args)–元组参数
fun3(*args),这传参方式是可以传入任意个参数,这些若干参数都被放到了tuple元组中赋值给行参args,之后要在函数中使用这些行参,直接操作args这个tuple元组就可以了,这样的好处是在参数的数量上没有了限制,但是因为是tuple,其本身还是有次序的,这就仍然存在一定的束缚,在对参数操作上也会有一些不便。
>>> def a(*x):print x
>>> a(1,2,3)
(1, 2, 3)
>>> a(x=1,y=2,z=3)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: a() got an unexpected keyword argument 'x'
注:不管有多少个,在函数内部都被存放在以形参名为标识符的元组中。
练习5:累计乘法
>>> def mul(*arg):
... result=1
... for i in arg:
... if not isinstance(i,(int,float,long)):
... return None
... else:
... result*=i
... return result
...
>>> mul(1,2,3,4)
24
fun4(**kargs)–参数是字典
fun4(*kargs)最为灵活,形参名前加两个表示,参数在函数内部将被存放在以形式名为标识符的dictionary中,这时调用函数的方法则需要采用arg1=value1,arg2=value2这样的形式。
>>> def a(**x):print x
>>> a(x=1,y=2,z=3)
{'y': 2, 'x': 1, 'z': 3}
>>> a(1,2,3)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: a() takes exactly 0 arguments (3 given)
综合练习3:mul(1,2,3,4,5,a=6,b=7)实现连乘函数,需要用到arg,*kw,且函数有固定2个参数:a,b
def mul(a, b, *arg, **kw):
result=1
if not isinstance(a, (int, float, long)) and not isinstance(b, (int, float, long)):
return None
else:
result=a*b
for i in arg:
if not isinstance(i, (int, float, long)):
return None
else:
result *= i
for j in kw.values():
if not isinstance(j, (int, float, long)):
return None
else:
result *= j
return result
调用函数的方式:
(1)mul(10,20,20,34,c=40,d=50)
(2)mul(10,20,(20,34),*{“c”:40,”d”:50})
(3)mul(10,20,30,40,f=20,(20,34),*{“c”:40,”d”:50})
等价于 tup=(20,34) dic={“c”:40,”d”:50}
mul(10,20,30,40,f=40,tup,*dic)
注意我们的元组和字典参数仅仅是被调函数中最终接收的元组和字典的子集
(四)匿名函数
python使用lambda来创建匿名函数。
(1)lambda只是一个表达式,函数体比def简单很多。
(2)lambda的主体是一个表达式,而不是一个代码块。仅仅能在lambda表达式中封装有限的逻辑进去。
(3)lambda函数拥有自己的命名空间,且不能访问自有参数列表之外或全局命名空间里的参数。
(4)虽然lambda函数看起来只能写一行,却不等同于C或C++的内联函数,后者的目的是调用函数时不占用栈内存从而增加运行效率。
(5)lambda函数返回一个函数对象,如果没有变量接受这个返回值,它很快就被丢弃,也正是由于lambda是一个表达式,所以它可以直接作为List,dict的成员
def与lambda区别:
注:在def中,用return可以返回的可以放在lambda后面,不能用return返回的不能定义在lambda后.因此,像if或for或print这种语句就不能用于lambda中,lambda一般只用来定义简单的函数。
语法:
lambda函数的语法只包含一个语句:
lambda[arga,arg2……]:expression
几个规则:
1.一般也就一行表达式,必须有返回值
- 不能有return
- 可以没有参数,可以有一个或多个参数
练习6:x的y次方
>>> sum=lambda x,y:x**y
>>> print sum(10,20)
练习7:
def make_repeater(n):
return lambda s: s*n
twice = make_repeater(2) #等价lambda s: s*2
print twice('word') ##等价lambda s: 'word'*2
print twice(5) ###等价lambda s: 5*2
(五)全局变量和局部变量
定义在函数内部的变量拥有一个局部作用域,定义在函数外的拥有全局作用域。
局部变量只能在其被声明的函数内部访问,而全局变量可以在整个程序范围内访问。调用函数时,所有在函数内声明的变量名称都将被加入到作用域中。
>>> total=0
>>> def sum(a,b):
... total=a+b
... print total
... return total
>>> sum(10,20) #结果 30 30
>>> print total #结果 0
>>> total=0
>>> def sum(a,b):
... global total
... total=a+b
... print total
... return total
>>> sum(10,20) #结果 30 30
>>> print total #结果 30
总结:
1 函数中使用全局变量,声明为global
2 否则当局部变量使用,且局部变量都要在函数开始的地方做赋值
3 如果函数中的变量没有声明为global,且在外部有同名的变量
则可以直接使用,如果进行了赋值操作,则此变量变为局部变量
且在使用前必须初始化赋值。