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

Python函数

2015-06-24 16:09 721 查看

关于函数

1. 一个好的习惯

在函数前面说明:

加注释 / 直接写上字符串(作为函数一部分:文档字符串)

def listx(string):
'change string to list'
return list(string)
print listx.__doc__


2. 函数返回值

return 可以返回值,然而只有return 的效果跟没有return是一样的:None

对于简单地变量,将其作为参数传递给函数,函数在内部改变(实际上这里的参数相当于函数内部的变量,然后传参相当于将值赋给函数内部变量)其值不会影响到该变量,同时字符串和元组不能被改变。

def norChange(c):
c = 'Change!'
change = 'No change'
norChange(change)
print change

#similar process with code above
change = 'No change'
c = change
c = 'Change!'
print change
而列表不是普通变量
listc = ['no change','no change']
def lChange(l):
l[0] = 'Change!'
lChange(listc)
print listc

#similar process with code above
listc = ['no change','no change']
l = listc
l[0] = 'Change!'
print listc
所以要想改变原值,则可以使用 return 返回赋值 / 使用列表作为参数。当需要返回两个值的时候,使用元组,列表,字典,都可以

3. 关键字参数

关键字参数可以提供参数的初始化,使用默认值:

def greet(greeting = 'Hello', name = 'world'):
print '%s,%s!' % (greeting,name)
greet()
greet(name = 'jessy')


4. 参数不定量的解决方法——收集参数

def print_params(x,y,z = 3,*pospar,**keypar):
print x,y,z
print pospar  # there is no *
print keypar  # there is no **

print_params(1,2,3,4,5,6,7,foo=1,bar= 2)
print
print_params(1,2)


其中可以看到,参数 z=3 是关键字参数,根据结果:

没有给收集参数赋值,将会得到一个空元组 / 字典

* 表示收集剩余的值(除了关键字参数),以元组形式表示

** 表示收集剩余的关键字参数,以字典形式表示(参数名为key,参数值为value)

所以针对不同的参数就有了不同的应对方案了。没有关键字参数、收集参数的就一定要给参数赋值

参数的应用:

注意 ** 针对的参数是关键字参数(param=x 的形式)

因为** 会将参数变成字典,而在print 后面可以看到其应用了格式化字符串的字典用法 %(key),而字典没有顺序,只按照关键字来查找值

def x(*num):
print num
x() # will print ()
x(1)
x(x=1)

def y(**num):
print num
y() # will print {}
y(1)
y(y=1)


注意上面的 x(x=1)  / y(1) 将会出错,因为定义x的时候参数是 *——不能是关键字参数;定义y的时候参数是**——必须是关键字参数

当然,如果使用字典,然后将字典作为参数传过去也是可以的,不过字典前面要加上 **,算是逆向操作(因为传**param到函数里呈现出来是字典),将其变成普通的关键字参数:

def story(**kwds):
print kwds
print 'Once upon a time, there was a %(job)s called %(name)s'\
% kwds

story(job='king', name='Gummy')
#dict has no sequence,key can find value
story(name='Sir Robin',job='brave knight')
params = {'job':'language', 'name':'Python'}
#story(params) can't work
story(**params)

del params['job'] # del: name and reference
#story(job='stroke of genius', params)   can't work
story(job='stroke of genius', **params)


同样,将本来是 tuple 当做参数加上 * 传值回去,也会变成普通的非关键字参数:

def power(x,y,*others):
print 'Receive redundant parameters', others
return pow(x,y)
print power(3,2)
print power(y=3,x=2)
params = (5,)*2
print power(*params) # tuple is empty
listx = [3,4,5,6]
print power(*listx)


5. 作用域

vars() ——将变量和值的关系形成字典

在函数内部引用全局变量的方法:

globals()['x'] ——返回一个全局变量的字典,通过全局变量名字查找value

global x 内部声明为全局变量

6. 递归函数

因为每次调用函数都会用掉内存,所以足够的函数调用发生后,内存空间不够——程序将以“超过最大深度”结束; 

递归函数大多数情况下都可以使用循环来代替,且循环可能更加有效率:

计算一个数的n次方,使用递归就需要先进行分析,找到特殊的情况(终止情况)

-对于任意数字 num 来说, num 的零次方是1

-对于任何大于0的数来说,num ^n = num^(n-1) * num

def power(num,n):
if n == 0:
result = 1 # n-1 until n ==0
return result
return num * power(num,n-1)
print power(2,3)使用循环会更简单:
def power(num,n):
result = 1
for i in range(n):
result *= num
return result
print power(2,3)

* reference & inspiration:网易云课程第四周

对于函数,我自己的简便方法便是:先看调用,再看定义。

对于递归,我自己的简便方法便是:看return语句,return点就是目的值。

其中调用函数的时候,一定要注意函数是否使用了return语句并且将函数值赋值给了相应的变量的,否则,全局变量是不会改变的。

常见混淆:(其中还有一个概念:局部变量 & 全局变量, 也就是说,函数中的局部变量如果不进行return赋值给全局变量的话,函数外的变量是不会受函数内的变量的影响的)

x = 1

def fun(x):
x = 2

fun(x)
print x


1. 递归函数

def gcd(m, n):
r = m % n
if r == 0:
return n
else:
r = m % n
return gcd(n, r)
print gcd(15, 36)


从这个函数中我获得的是:参数的传递,不仅可以在当前函数中使用参数,还可以将参数继续传递下去,于是直接影响到的就是一道关于斐波那契数列的求解。

斐波那契数列中,需要当前值与前面的一个sum值相加,要获取前面的sum值,就可以使用递归函数传递参数,实现接力棒一样的传递效果。

使用递归函数的时候,因为递归一般是实际问题从后往前(从结果到开始),所以,要注意return的使用(终止点,一般也就是实际问题的初始化),纯粹的调用函数没有 “return” 自己可能会返回value的值是None。

如下:

def bi_search(x, low, high):
guess = (low + high) / 2
print lst[guess], x
if (x == lst[guess]):
return guess
elif low > high:
return -1
elif lst[guess] > x:
bi_search(x, low, guess -1)
elif lst[guess] < x:
bi_search(x, guess + 1, high)
上述函数就是只在前两个判断中 return了。

递归函数中很容易认为,只需要在最后一个结束的地方-需要返回一个具体的值的地方 return 就OK了,但实际上调用递归函数的自身的时候也需要 return。

递归函数跟循环一样,都需要一个终止值,只不过表现得方式不一样。

sub1.  一个斐波那契数列的前10项为 :1, 2, 3, 5, 8, 13, 21, 34, 55, 89,对于一个最大项的值不超过n的斐波那契数列,求值为偶数的项的和。

解:

def fi(fir, second, top, allSum):
sum = fir + second
if (second % 2 == 0):
allSum += second
print "now the sum of even num is:", allSum
print second #debugging
if(sum > top):
return allSum #return teminate all function process
else:
return fi(second, sum, top, allSum)
#Just invoke the function without "return" will get value of "None"
#http://bbs.csdn.net/topics/390139774

sum0 = fi(1, 2, 100, 0)
print "The result is: ",sum0

当然,还有一个递归形式的斐波那契数列是大家认识的、比较常用的:

这种方式是通过数学观察直接得出的程序——

首先,分析斐波那契数列得到规律(一般情况):f(n) = f( n-1 ) + f( n-2 )  

其次,分析初始条件(特殊情况:一般都用递归中的最后一个return项,一般用作终止递归的条件):第一、第二项的值均为1

def f(n):
if n == 1  or n == 2:
return 1
else:
return f(n - 2) + f(n - 1)

print f(7)


* 说一句题外话:这样的分析实际上与中学学习的构造函数的方式十分的一致,今后也可以考虑使用数学分析的方法来解决程序的问题,毕竟程序语言只是一种工具,而数学思想才是核心

sub2. 非递归斐波那契数列(上面的代码仅仅只是为了介绍关于递归的一种用法,实际上对斐波那契数列的实现还有其他方式)

a. 循环实现:

def fib(n):
sum = 0
f1, f2 = 0, 1
while f2 < n:
if(f2 % 2 == 0 ):
sum += f2
f1, f2 = f2, f1 + f2
return sum
print fib(100)

以上为抛砖引玉,实际上斐波那契数列的解决方式还有很多:

Python yield 使用浅析

b. 列表实现:

def fibs(num):
result = [0,1]
for i in range(num-2):
result.append(result[-2]+result[-1])
return result
print fibs(10),sum(fibs(10))


sub3. 递归求一个数字n的二进制

对于一个数字n求二进制,基础算法是:数字n / 2,得到余数,得到除数;除数再除以2,得到余数,得到除数...从上到下除,直到除得的数为0或者1,然后余数从下往上依次排列,最后得到的余数排列在最左侧,也就是最大的位数,最先得到的余数排列在最右侧。

那么可以知道:

初始条件(用作终止递归)是除数为0 or 1 —— 实际上每得到一个余数,最后的余数(也就是初始条件返回的值)就向左移一位也就是 x 10;后得到的余数要比之前得到的余数大一个数量级(因为是自底向上排列)

普通规律:后得到的一个数向左移动一位,再加上当前的余数

def binary(n):
if( n == 0 or n == 1):
return n
else:
return binary(n / 2) *10 + n % 2


2. 若已知1800年1月1日为星期3,则对于一个给定的年份和月份,输出这个月的最后一天是星期几。

解:

def leapYear(year):
if year % 4 ==0 and year % 100 != 0 or year % 400 ==0:
return 366
else:
return 365

def getMonth(month, year):
if month in [1, 3, 5, 7, 8, 10, 12]:
return 31
elif month in [4, 6, 9, 11]:
return 30
elif month == 2:
if leapYear(year) == 365:
return 28
else:
return 29
else:
print 'Error'

y = raw_input("year:")
m = raw_input("month:")

year = int(y)
month = int(m)

yearCount = 0
monthCount = 0
totalDay = 0

if(year - 1800 > 0):
for i in range(1800, year):#bug is "year+1",add excess year,can't count the year you input.
yearCount += leapYear(i)
print 'year is :', i #debugging
for j in range(1, month + 1):
monthCount += getMonth(j, year)
totalDay = monthCount +  yearCount - 1 #coz the known date is 1st
print totalDay  #debugging
result0 = (totalDay % 7 + 3) % 7
print result0


对于星期类的题目,最重要的就是需要算出与给出星期之间所差的天数,在算天数的时候有一个需要注意的是闰年。

算出了所差天数之后,就需要看% 7所差的天数了,7天是一个星期周期,没有余数,就正好与给出的星期数一致。

此题的解法还是跟之前提到过的黑盒思想一样的,分函数解决问题,这个思想还要感谢我的同事ZHT在解决问题时给我的启发。

看到这里,可以发现,我惯用的纠错方法是在合适的位置添加 print。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: