python基础教程九 抽象四(函数参数终)

文章预览:

  • 1. 分配参数
  • 2. 作用域
  • 3. 递归
    • 1. 俩个经典案例:阶层和幂
    • 2. 递归的意义
    • 1. 递归二分查找
        • 1. 二分查找的条件
        • 1. 二分查找的意义

1. 分配参数

前面介绍了如何将参数收集到元组和字典当中,但同样的俩个运算符,也可以执行相反的操作。与收集参数相反的操作是分配。假设有如下函数:

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

如果在定义和调用函数时都使用*或**,将只传递元组和字典。因此还不如不使用它们。

2. 作用域

所谓的作用域就是变量生效的范围,那么有多少个命名空间呢?除全局作用域外,每次函数调用都将创建一个。

>>> 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

3. 递归

递归是函数引用自身的一种方式,主要是依靠系统栈去帮助完成的。
递归分为递归条件和基线条件,基线条件是满足这种条件时函数将直接返回一个值。递归条件是包含一个或多个调用。这些调用旨在解决问题的一部分。

1. 俩个经典案例:阶层和幂

首先探讨一个问题,将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

幂的方式大同小异,不再论述。

2. 递归的意义

递归可以不能转换成循环吗?大多是是可以的,而且转换完效率更高。然而,在很多情况下,使用递归的可读性更高,而有时要高很多,在你理解了函数的递归式定义尤其如此。但是递归有几项弱点要注意,举例递归本质是用系统栈作为临时的保存,那系统栈是无限大的吗?肯定不是,递归深度达到一定量的时候会崩溃程序,还有有些算法用递归可能一直在重复运算,最明显的就是斐波那契数列,f(n)=f(n-)+f(n-2) 你这样其实一直在重复某些运算,是算法复杂度提升。所以是否要用递归需要具体情况具体分析。

1. 递归二分查找

二分查找也可以使用递归实现,二分查找就是每次我都把范围缩小当前的一半,简单来说就是128个数看中间值和目标数大小关系去掉一半变成64,之后再用中间的比看大小关系直到,找到最后一个,当然也可能在当前数列当中并不存在那就返回-1,经过我的陈述,你已经发现了,如果能用中间的数进行代表,说明了什么,最起码说明在序列当中必须有序,如果原始数据没序,必须进行排序,才可以使用二分法。

1. 二分查找的条件

那我们来分析一下 递归条件和基线条件。

递归条件: 只要我和当前数比了 大了,小了 我就把寻找范围缩短一半,放在下个函数继续调用
基线条件 这个条件表示的是达到什么情况下我就停止,要不然就是无限循环了或者栈溢出,我们设数列的起始索引为start,终点索引为end,必然有startend 那便是终止条件或者说基线条件当中的没找到,如果mid处的值刚好得到目标值,那便是命中的基线条件。目标代码如下:

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))

1. 二分查找的意义

假设原始数据1024个是有序情况,你用顺序查找最坏的情况要查找1024次,而用二分查找最坏的次数为10次,数据量越大,在有序的情况下二分查找越有利 log ⁡ 2 n \log_2^n log2n

假设原始数据1024个是无序情况,你用顺序查找最坏的情况要查找1024次,而用二分查找首先要排序,我们简单算,是近似数(或者说增长的幅度),不是准确数,用快排排序,就是n* log ⁡ 2 n \log_2^n log2n 排序次数大概 10240,再加上查找的次数10次,查找的次数反而好像更多了,但是排序是一次性就能搞定的,当你很多次查找的时候,总次数 排序加二分还是少的。所以在做算法题的时候还是要用排序 加二分方法。

你可能感兴趣的:(python基础教程(第三版),python,开发语言)