每一个具体问题都有具体的实现方法,而对于同一类问题,不同的具体方法可以抽象成这一类的方法,当给定一个特定的输入时,这一类方法会变成适用于一具体问题的具体方法。
这就是函数想要做的事,将一类方法简化抽象,使得每次都可以通过该函数实现想要实现的功能。
python内置很多函数,例如:
abs(),求绝对值的函数;
cmp(x, y),比较x, y之间大小的函数等等
还可以自己定义自己想要的函数:
如下:
def my_abs(x): if x >= 0: return x else: return -x
定义函数以def语句开头,依次写出函数名、括号、括号中的参数和冒号:
,然后,在缩进块中编写函数体,函数的返回值用return
语句返回。
函数什么都不做,就是空函数,如下:
def nop(): pass
实际上空函数中,pass
可以用来作为占位符,比如现在还没想好怎么写函数的代码,就可以先放一个pass
,让代码能运行起来。
在设计函数的参数时,应注意必选参数在前,默认参数在后。默认参数可以简化函数的调用:
def power(x, n=2): s = 1 while n > 0: n = n - 1 s = s * x return s
定义一个默认幂数是2的求幂函数后,只输入一个参数时,就默认幂数为2,输入两个参数,则第二个参数为新的幂数:
>>> power(5) 25 >>> power(5, 2) 25
但是注意这里默认参数得是不可变的,而不能是一个变量,不然重复调用时会出现错误。
def add_end(L=[]): L.append(‘END‘) return L
第一次使用默认参数调用时,结果还是对的
>>> add_end() [‘END‘]
但是多次调用就明显出错了
>>> add_end() [‘END‘, ‘END‘] >>> add_end() [‘END‘, ‘END‘, ‘END‘]
这是因为原函数中的默认参数时[],它实质上是一个list的变量,会随着每次使用而改变。这里表示空的list常量,应该用“L=None”
如果函数的输入的个数不确定,则使用可变参数*args;如果传入的参数是一个dict类型,则应使用关键字参数**kw
前者是将多个参数封装成tuple,后者是将这些封装成一个dict。所以前者得传入一个个单个的量,后者得传入key-value键值对。
当要一起用到必选参数、默认参数、可变参数、关键字参数时,应注意参数定义的顺序是:
必选参数、默认参数、可变参数、关键字参数。
递归函数中每次进入一个函数调用会增加一层栈帧,每当函数返回时,栈就会减少一层,这样递归函数就可能会出现栈溢出。
解决递归调用栈溢出的方法是通过尾递归优化。尾递归是指,在函数返回的时候,调用自身本身,并且,return语句不能包含表达式。这样,编译器或者解释器就可以把尾递归做优化,使递归本身无论调用多少次,都只占用一个栈帧,不会出现栈溢出的情况。
遗憾的是,大多数编程语言没有针对尾递归做优化,Python解释器也没有做优化==