前面介绍了如何将参数收集到元组和字典当中,但同样的俩个运算符,也可以执行相反的操作。与收集参数相反的操作是分配。假设有如下函数:
def add(x,y):
return x+y
同时假设还有一个元组,其中包含俩个你要相加的数。
params=(1,2)
这与前面执行的操作差不多是相反的:不是收集参数,而是分配参数。这是通过在调用函数时使用运算符*实现的。
add(params)
3
这种做法也可以用于参数列表的一部分,条件是这部分位于参数列表的末尾。使用运算符*,可将字典中的值分配给关键字参数,例如
>>> def output(name='LI'):
... print(name)
...
>>> s={'name':'yuan'}
>>> output(**s)
yuan
如果在定义和调用函数时都使用*或**,将只传递元组和字典。因此还不如不使用它们。
所谓的作用域就是变量生效的范围,那么有多少个命名空间呢?除全局作用域外,每次函数调用都将创建一个。
>>> def foo():x=42
...
>>> x=1
>>> foo()
>>> x
1
>>>
在这里,函数foo修改了(重新关联)了变量x,但当你最终查看的时候,它根本没变。这是因为调用foo时创建了一个新的命名空间,供foo的代码使用。赋值语句x=42是在这个内部作用域中执行的,不影响外部作用域内的x。在函数内使用的变量称为局部变量。
但如果在函数中访问全局变量呢?如果只是想读取这种变量的值(不重新关联它)。通常不会有任何问题。
>>> def c(string):
... print(string+a)
...
>>> a='ssss'
>>> c('aaaaaa')
aaaaaassss
像这样访问全局变量是众多bug的根源,务必慎用全局变量。
读取全局变量的值通常不会有问题,但还是存在出现问题的可能性,如果有一个局部变量与全局变量重名,就无法直接访问全局变量,因为它被局部变量覆盖了。
如果需要,可使用函数globals来访问全局变量例如:
>>> def combine(para):
... print(para+globals()['para'])
...
>>> para='2222'
>>> combine('1111')
11112222
重新关联全局变量是另一码事。在函数内部给变量赋值时,该变量是局部变量,除非你明确地告诉python它是全局变量。global关键字
>>> x=1
>>> def change_global():
... global x
... x=x+1
...
>>> change_global()
>>> x
2
递归是函数引用自身的一种方式,主要是依靠系统栈去帮助完成的。
递归分为递归条件和基线条件,基线条件是满足这种条件时函数将直接返回一个值。递归条件是包含一个或多个调用。这些调用旨在解决问题的一部分。
首先探讨一个问题,将n个人排成一队有多少种方式。
可以使用循环
>>> def fac(num):
... f=1
... for i in range(1,num+1):
... f*=i
... return f
...
>>> fac(5)
120
这种实现可行而且直接了当,递归的方式关键在于阶乘的数学定义,表示如下.
1的阶乘是1
对于大于1的数字n,其阶乘为n-1的阶层再乘以n。
理解了这个定义,实现起来就很容易。
>>> def fac(num):
... if num==1:
... return 1
... else:
... return num*fac(num-1)
...
>>> fac(5)
120
幂的方式大同小异,不再论述。
递归可以不能转换成循环吗?大多是是可以的,而且转换完效率更高。然而,在很多情况下,使用递归的可读性更高,而有时要高很多,在你理解了函数的递归式定义尤其如此。但是递归有几项弱点要注意,举例递归本质是用系统栈作为临时的保存,那系统栈是无限大的吗?肯定不是,递归深度达到一定量的时候会崩溃程序,还有有些算法用递归可能一直在重复运算,最明显的就是斐波那契数列,f(n)=f(n-)+f(n-2) 你这样其实一直在重复某些运算,是算法复杂度提升。所以是否要用递归需要具体情况具体分析。
二分查找也可以使用递归实现,二分查找就是每次我都把范围缩小当前的一半,简单来说就是128个数看中间值和目标数大小关系去掉一半变成64,之后再用中间的比看大小关系直到,找到最后一个,当然也可能在当前数列当中并不存在那就返回-1,经过我的陈述,你已经发现了,如果能用中间的数进行代表,说明了什么,最起码说明在序列当中必须有序,如果原始数据没序,必须进行排序,才可以使用二分法。
那我们来分析一下 递归条件和基线条件。
递归条件: 只要我和当前数比了 大了,小了 我就把寻找范围缩短一半,放在下个函数继续调用
基线条件 这个条件表示的是达到什么情况下我就停止,要不然就是无限循环了或者栈溢出,我们设数列的起始索引为start,终点索引为end,必然有start
def erfen(a,mubiao,start,end):
if start>end:
return -1
else:
mid=(start+end)//2
if(a[mid]==mubiao):
return mid
elif a[mid]>mubiao:
end=mid-1
elif a[mid]<mubiao:
start=mid+1
return erfen(a,mubiao,start,end)
a=[1,2,3,4,5,6]
while(True):
m=int(input())
print(erfen(a,m,0,len(a)-1))
假设原始数据1024个是有序情况,你用顺序查找最坏的情况要查找1024次,而用二分查找最坏的次数为10次,数据量越大,在有序的情况下二分查找越有利 log 2 n \log_2^n log2n
假设原始数据1024个是无序情况,你用顺序查找最坏的情况要查找1024次,而用二分查找首先要排序,我们简单算,是近似数(或者说增长的幅度),不是准确数,用快排排序,就是n* log 2 n \log_2^n log2n 排序次数大概 10240,再加上查找的次数10次,查找的次数反而好像更多了,但是排序是一次性就能搞定的,当你很多次查找的时候,总次数 排序加二分还是少的。所以在做算法题的时候还是要用排序 加二分方法。