一、函数

1.1 语法格式、参数、返回值

定义函数的语法格式如下:

def 函数名(形参列表):

        函数体

        return 返回值

调用的格式如下:

函数名(实参列表)

或者也可以这样调用:

返回值 = 函数名(实参列表)

Python中要求,函数定义写在前面,函数调用写在后面。

Python中要求形参和实参的个数必须匹配,对参数类型没有要求(动态类型);只要保证传入的参数类型在函数体里面能够支持对应的运算操作即可。

def calcSum(beg, end):
    theSum = 0
    for i in range(beg, end + 1):
        theSum += i
    return theSum

print(calcSum(1,100))
print(calcSum(300,400))
print(calcSum(1,1000))

一个通用的编程原则:一个函数只做一件事。比如,上述代码中我们只让函数完成计算这一件事情,把打印的逻辑放在了函数的外面(让“逻辑”和“界面/交互”分离了)。可以起到“解耦合”的作用,也就是,一方产生变动,对另一方影响不大。

一个函数中是可以有多个return语句的,比如:

def isOdd(num):
    if num % 2 == 0:
        return False
    else:
        return True

也可以写成这样:

def isOdd(num):
    if num % 2 == 0:
        return False
    return True

print(isOdd(100))
print(isOdd(101))

虽然return True在条件语句的外面,但是如果满足条件,在条件语句内部就返回了,执行不到return True这一句,所以这样写也是可以的。

Python中一个函数可以返回多个值,这个特性C++和Java都没有。

(C++中想要让函数返回多个值,只能通过输出型参数(指针/引用)

Java中想要返回多个值,需要把多个值包装成一个对象,返回这个对象。)

如果函数有多个返回值但只想要其中的一个,可以在赋值的时候把不要的用下划线代替。

以下是python中一个函数返回多个值的例子。

def getPoint():
    x = 10
    y = 20
    return x, y

a, b = getPoint()
_, c = getPoint()
print(a, b)
print(c)

1.2 变量作用域

函数内部和函数外部是可以使用同名变量的。虽然变量名相同,但是是不同的变量。函数内部的变量名,只能在函数内部生效,出了函数就无效了(局部变量)。而函数里是可以使用全局变量的。

当函数中尝试访问某个变量的时候,会先尝试在局部变量中查找,如果找到就直接访问;如果没找到就会往上一级作用域中进行查找。 

如果想要在函数内部创建或修改全局变量,需要使用global关键字声明。

if,else,for这些关键字也会引入代码块,但是这些代码块不会对变量的作用域产生影响,在上述语句代码块中定义的变量,可以在外面被访问。

1.3 函数执行过程

函数体的内容只有在函数被调用的时候才会执行。

程序执行到函数调用的时候,就会跳转到函数体内部来进行执行。当函数内部执行完毕(执行完了/遇到return),就回到之前调用的位置,继续往下执行。

1.4 链式调用和嵌套调用

链式调用就是用一个函数的返回值作为另一个函数的参数。举个例子:

def isOdd(num):
    if num % 2 == 0:
        return False
    return True

def add(x, y):
    return(x + y)

print(isOdd(add(1, 2)))

链式调用中,是先执行()里面的函数,后执行外面的函数。换句话说,调用一个函数,就需要先对它的参数求值。

需要注意,链式调用的时候不要嵌套得层次太深,会影响可读性。

嵌套调用指的是,在一个函数内部,还可以调用其他的函数,比如:

def a():
    print('函数a')

def b():
    print('函数b')
    a()

def c():
    print('函数c')
    b()

def d():
    print('函数d')
    c()

d()

如果每个函数把调用和打印的顺序反过来,得到以下代码和运行结果:

def a():
    print('函数a')

def b():
    a()
    print('函数b')

def c():
    b()
    print('函数c')

def d():
    c()
    print('函数d')

d()

1.5 局部变量和函数栈帧

pycharm调试器的左下角可以看到函数之间的“调用栈”。调用栈里描述了当前代码的函数之间的调用关系是什么。每一层调用关系就称为“函数的栈帧”,每个函数的局部变量就在各自的栈帧中体现。比如我们在刚才代码的基础上,每个函数里创建一个局部变量:

def a():
    num1 = 10
    print('函数a')

def b():
    num2 = 20
    a()
    print('函数b')

def c():
    num3 = 30
    b()
    print('函数c')

def d():
    num4 = 40
    c()
    print('函数d')

d()

调试起来之后,选中左侧的栈帧,就可以看到里面的局部变量。

调用函数,则生成对应的栈帧。函数结束,则对应的栈帧消亡(里面的局部变量也就没了。)

如果我们将上述代码中的num1-num4全都重命名为num,它们仍然是不同的变量,是保存在各自的栈帧中的。

1.6 函数递归

函数递归就是函数自己调用自己。举个例子,用递归来写一个计算阶乘的函数:

def factor(n):
    if n == 1:
        return 1
    else:
        return n * factor(n-1)

print(factor(5))

递归函数进行了这样的执行过程

以上递归函数循环调用自己,直到满足n == 1这个条件的时候开始逐层返回。

递归代码很多时候看上去写法很简单,但是执行过程非常复杂,在分析递归代码的时候,可以借助画图和调试器来分析。

递归代码有两个要素:

1、递归的结束条件

2、递归的递推公式

有点类似数学归纳法。

递归的缺点是,执行过程复杂,难以理解;以及容易出现“栈溢出”情况。

递归代码一般都可以转换成等价的循环代码,而且循环的版本通常运行速度要比递归版本有优势,因为函数调用是有开销的。递归的优点在于,代码非常简洁,尤其是在处理一些本身就由递归来定义的问题时比较方便。

1.7 函数默认值

Python中的函数,可以给形参指定默认值。带有默认值的参数,可以在调用的时候不传参。

def add(x, y,debug = False):
    if debug:
        print(f'x = {x}, y = {y}')
    return x + y

result1 = add(10, 20, True)
print(result1)
print('--------------------------------------')
result2 = add(10, 20, False)
print(result2)

C++也支持形参默认参数,而Java就不支持。

注意,python中要求带有默认参数的形参不能放在无默认参数的形参前面。

1.8 函数的关键字参数

按照先后顺序来传参的传参风格称为“位置参数”,是各个编程语言中最普遍的方式。而Python中还有另一种传参方式:按照形参的名字来进行传参。

位置参数和关键字参数还可以混着用,只不过混着用的时候要求未知参数在前,关键字参数在后。

关键字参数一般是搭配默认参数来使用的。一个函数可以提供很多参数,实现对函数内部功能做出调整设定。为了降低调用者的使用成本,可以给大部分参数设出默认值。当调用者需要调整其中一部分参数时,就可以搭配关键字参数进行操作。

END

Logo

这里是“一人公司”的成长家园。我们提供从产品曝光、技术变现到法律财税的全栈内容,并连接云服务、办公空间等稀缺资源,助你专注创造,无忧运营。

更多推荐