第六章 抽象

6.1 懒惰是一种美德

为避免代码中出现大量重复代码,我们需要使用函数的相关知识。

6.2 抽象和结构

使用函数(或其他抽象相关)不仅仅是为了让代码量变少,也是为了让代码更易于别人理解。

6.3 自定义函数

使用def关键字来创建函数,举例,裴波那契数(每一个值是前两个值之和):

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

你还可以使用callable()来检验一个函数是否可以调用

6.3.1 给函数编写文档

除了使用#添加注释外,我们还可以给函数添加专有的注释:

def fibs(num):
    '求裴波那契数列'
    result = [0, 1]
    for i in range(num - 2):
        result.append(result[-2]+result[-1])
    return result
#使用_doc_来查看函数注释
print(fibs._doc_)
#也可以使用help函数来查看函数相关信息
print(help(fibs))

6.3.2 其实并不是函数的函数

其实,,函数不一定有返回值。

6.4 参数魔法

关于参数,如果你理解c中的指针和函数,此部分将会很好理解

6.4.1 值从哪里来

在函数定义时,也就是是函数名后面的参数,我们一般叫做形参,使用函数时传进的数为实参。当你在定义函数时,通常不用担心值从何处来,你只需要考虑接受正确的值并处理。

6.4.2 我能修改参数吗

我决定从三种情况来谈一下这个问题。

#普通变量,在函数中的变化并不影响原来的值
>>> def fun(n):
        n = "ABC"    
>>> name = "DEF"
>>> fun(name)
>>> name
'DEF'
#字符串,元组等时不可变的,当然你在函数中修改他们也没有用
#可变的数据结构,如列表等,在函数中的改变会影响函数外的值
>>> def f(n):
        n[0] = 'A'
>>> names = ['a','b','c']
>>> f(names)
>>> names
['A', 'b', 'c']

当然,这种规则下,有些操作时不够方便的,比如我想要在函数中修改普通变量的值,也改变函数外该变量的值,我们通常会用返回值将修改后的数据提供给函数外变量。

6.4.3 关键字参数和默认值

其实我们前面使用的参数多是位置参数(即系统根据你提供变量的位置来一一对应),不过这种方法并不适用于所有情况,我们还会经常用到关键字参数,即使用名称指定参数。

# 位置参数:
def hello(gretting, name):
    print('{},{}!'.format(gretting, name))
hello('Hi', 'Tom')
# 关键字参数:假如你打算打招呼的方式默认是“Hello”,你可以这样。
def hello_2(gretting ='hello', name='world'):
    print('{},{}'.format(gretting, name))
hello_2()
hello_2('Hi', 'Tony')
hello_2(name='Tom')

当然,你可以结合关键字参数和位置参数一起用(虽然不推荐这么用)

6.4.4 收集参数

如果有时候你不知道用户会输入多少参数,那么可以用*收集起来(好吧,这和前面的赋值有点像)

def print_str(*str):
    print(str)
print_str(1, 5, 7,24 ,56)

(好吧,我承认这函数没卵用)

这里的输出结果是一个元组(),而不是列表[]。

同样的,*变量可以放在任何位置收集参数,当然,在*变量后面有参数时,需要指定关键字,防止出现歧义。

星号不会收集关键字参数,如果你非常想收集,你可以使用两个星号**,但这样你得到的会是一个字典。

6.4.5 分配参数

既然有收集,就会有分配参数。同样是使用星号。

def add(x, y):
    return x+y
n = (1, 2)
print(add(*n))

当然,这种做法看起来有点蠢,但它是有其特定的使用环境的,我们将在第九章中讲到。

6.5 作用域

当你创建了一个函数时,就创建了一个命名空间,函数中定义的变量等可以认为都是存储在这个命名空间中的,这些变量的调用不会影响到全局变量。当参数中的局部变量与全局变量同名时,以局部变量为准,如果你想指明使用全局变量,可以使用globals()['全局变量名'],或者global 变量名。

另外,很神奇的是,python是支持函数嵌套使用的(c语言:mmp)

6.6 递归

递归即自己调用自己,这看上去有点难以理解,不过当你使用起来,你会喜欢上这种简洁的写法。递归函数一般会有下面两部分:

(1)基线条件:满足这一条件函数将直接返回一个值。(这样就避免了无限调用的可能)

(2)递归条件:包含一个或多个调用,通常是解决问题的一部分。

当然,这些对于第一次接触的人来说,还是有些懵,那我们就来看一下两个典型的例子:

6.6.1 两个经典案例:阶乘和幂

阶乘:当然,你可以用循环的思想来写,像下面这样

def factorial(n):
    result = n
    for i in range(1,n):
        result *= i
    return result

这里我们换一种思路,用递归来实现:

def factorial(n):
    if n == 0:      #基线条件,满足即退出函数
        return 1
    else:
        return  n*factorial(n-1)

我们再来定义幂的运算(就是和内置函数pow一样的效果)

def power(x, n):
    if n == 0:
        return  1
    else:
        return x * power(x, n-1)

当然,你可以明显的看到,递归大部分情况是可以用循环代替的,而且循环在时间复杂度可能更好一点,但是当你掌握了递归,你就会爱上这种简洁的表达方式。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值