软件工程中有个DRY (Don’t Repeat Yourself)原则,就是说我们的代码需要具备可复用性,意味着如果我们想要重复使用一段代码,不能用土方法到处复制粘贴。Function功能将一堆代码抽象成一个函数,只有调用函数的时候,代码才会运行。函数的编辑使代码可以被一次编辑,多次调用。以下是Python中函数的定义语法:

def function_name(parameters):
    expressions

Python使用def(define)开始函数定义,紧接着是函数名,括号内部为函数的参数,冒号之后缩进的部分是函数内的代码块,如果想要函数有返回值,在expressions中的代码中用return返回。

# Defining a function
def myFunction():
    a = 1 + 2
    print(f"sum is {a}")

myFunction() # Calling a function

上面我们定义了一个名为myFunction的函数,函数没有接受参数,所以括号为空,紧接着的是函数的功能代码。前4行只是定义了函数,并没有执行函数。之后我们输入myFunction()调用函数,注意这里调用函数的括号不能省略。调用函数后输出结果是 sum is 3。如果以后想要再调用此函数,只需要再调用myFunction() 就好了。

参数传递和返回值

def function_name(parameters):
    expressions

parameters的位置就是函数的参数,在调用的时候传入即可。

# Positional Arguments
def describe_person(name, age):
  print(f'{name} is {age} years old.')
describe_person('enoch', 25)

# Multiple Function Calls
describe_person('enoch', 23)
describe_person('enoch', 26)

# Order matters in positional arguments
describe_person('enoch', 25)

# Order doesn't matter if you specify keyword arguments
describe_person(age=25, name='enoch')

这里我们定义了一个处理两个数字的的函数,函数功能就是把参数打印出来。但我们在调用的时候,如果不指定参数name和age,程序将会出错。在调用函数的时候,参数的数量和位置要按照函数定义。如果忘记函数的参数位置,在调用的过程中指明特定的参数值,也就是关键字参数,函数的运行也是不会受影响的。

我们也可以为函数提供备选的参数,在调用函数时,如果不传递参数,函数将会使用设定好的默认参数:

# Default values
def describe_pet(pet_name, animal_type='dog'):
  print(f'{pet_name} is a {animal_type}')

describe_pet('doudou')
describe_pet('duoduo', 'cat')

除了基本的数据类型,我们也可以将列表等其他数据类型当做参数传入:

fruits = ["a", "b", "c"]
def myFun(food):
    for x in food:
        print(food)

# Modifying a List in a Function
def set_to_one(input):
  for i in range(0, len(input)):
    input[i] = 1

number_list = [1, 2, 3, 4, 5]
set_to_one(number_list)
print(number_list)

# Preventing a Function from modifying a list
number_list2 = [1, 2, 3, 4, 5]
set_to_one(number_list2[:])
print(number_list2)

可变参数

如果我们有很多参数要传入函数,可以将这些参数封装成一个list或tuple传入,但这样不够pythonic。我们可以使用可变参数,可变参数代表传入的参数量是不定量的。注意可变参数的定义不能出现在特定参数和默认参数前面,否则会吞噬掉这些参数,以下是可变参数的实例:

def report(name, *grades):
    total_grade = 0
    for grade in grades:
        total_grade += grade
    print(name, 'total grade is ', total_grade)

report('Ben', 80, 90, 85)

这里的参数grades前面有*修饰,代表该参数是一个可变参数。那么当我们调用函数的时候,我们可以在name之后输入多个参数,这些参数被调用的时候,就会被函数迭代。

关键字参数

我们可以使用关键字参数传入任意个含参数名的参数,这些参数名在函数定义时不用指定,在函数内部这些参数会被自动封装成字典类型(dict):

def info(name, **kw):
    print('name is', name)
    for k,v in kw.items():
        print(k, v)

info('Mike', age=24, country='China', education='bachelor')

这里我们使用**修饰关键字参数kw,表明了该参数是关键字参数。通过可变参数和关键字参数,任何参数都可以用 unive1rsal_func(*args, **kw)表示。

Return语句

Return语句用于退出函数,并返回一个指定值。也就是将结果返回到调用的地方,并把程序控制权一起返回。不带参数值的return语句默认返回None。以下是return的演示:

# Returning a simple value
def get_formatted_name(first_name, last_name):
  return f'{last_name.title()} {first_name.title()}'

print(get_formatted_name('hao', 'zheng'))

使用条件判断,我们可以根据不同参数返回不同的内容:

# Making an argument optional
def proposal(name, age):
  if age >= 18:
    return f'{name}, I want to marry you.'
  else:
    return f'{name}, I can\'t marry you right now'

print(proposal('Emily', 16))
print(proposal('Emily', 20))

返回一个字典:

# Returning a dictionary
def build_person(first_name, last_name):
  person = {'first': first_name, 'last': last_name}
  return person

musician = build_person('hao', 'zheng')
print(musician['first'] + ' ' + musician['last'])

函数名规范

如果你给一个变量设定默认值,那么等号的两边是不应该有空格的,调用的时候也是一样:

def function_name(parameter_0, parameter_1='default value')
function_name(value_0, parameter_1='value')

如果有很多参数,那么其他行参数的缩进应该与第一行的参数缩进对应:

def function_name(
    parameter_0, parameter_1, parameter_2,
    parameter_3, parameter_4, parameter_5):
    function body...

局部和全局变量

在def中,我们可以定义一个局部变量,这个变量x只能在函数fun中有效。出了函数,x就不能被调用了。而且即使在函数中修改了相同名字的变量,出了函数,修改是起不到作用的:

y = 2
def fun():
    x = 1
    y = 1
    print(x)

print(y)
print(x)

如何在函数内部修改某个变量,并让这个修改保下去呢?我们在内部调用的时候,就要使用global关键字,这样我们在函数内部的操作也会保存下来:

x = 2
def fun()
    global x
    x = 1
fun()
print(x)

模块函数调用

我们先在名为pizza.py的文件中创建两个函数:

def make_pizza(size, type):
     print(f"A {size} inch pizza with {type} is ready for you.")

如果我们想在其他文件调用pizza.py中的函数时,使用import语句就好了:

# Importing an Entire Module (pizza.py)
import pizza
pizza.make_pizza(16, 'pepperoni')

# Using as to Give a Module an Alias
import pizza as p
p.make_pizza(16, 'cheese')

# Importing Specific Functions
from pizza import make_pizza
make_pizza(16, 'cheese')

# Using as to Give a Function an Alias
from pizza import make_pizza as mp
mp(16, 'cheese')

# Importing All Functions in a Module
from pizza import *
make_pizza(16, 'green peppers')

Lamda函数

Lambda函数也叫做匿名函数,及没有具体名称的函数。一个Lambda函数可以使用任意多的参数,但是只能有一个表达式:

x = lambda a, b, c: a + b + c
print(x(5, 6, 2)) # 12

为什么使用Lamda函数?当我们把lambda函数放在一个函数内部使用的时候,lambda可以很简洁地帮助我们实现小功能:

def myFun(n):
    return lambda a: a * n
myTripler = myFun(3)
print(myTripler(11))

递归函数

递归函数就是一个调用自己本身的函数。如果我们要计算阶乘 n! = 1 x 2 x 3 x … x n,用函数fact(n)表示则为:fact(n) = n! = 1 x 2 x 3 x … x (n-1) x n = (n-1)! x n = fact(n – 1) x n,用递归的方式写出来就是:

def fact(n):
    if n == 1:
        return 1
    return n * fact(n - 1)

递归函数逻辑清晰,定义也简单。理论上所有递归函数都可以写成循环的方式,但逻辑不如递归清晰。

程序入口判断

有时我们希望某文件被当做模板引进的时候,部分代码不要被执行。如果当前脚本含有单元测试,那么我们在其他地方调用此脚本的时候,测试部分就没有必要了。我们可以做以下的条件判断:

if __name__ == '__main__':
    # statements

如果当前文件只是被作为模块引进的话,在程序运行时,statements部分的代码是不会被执行的。

课后练习

创建三个列表 list1, list2, sum,创建一个函数将list1,list2中的每个元素依次相加,并存入sum列表中。比如list1 = [1, 2, 3], list2 = [3, 7, 9], 那运行函数后,sum会变成[4, 9, 12]。(备注:如果两个列表长度不同,就将较长列表的多出部分直接加给sum。)

list1 = [1, 2, 3, 5]
list2 = [2, 4, 9, 10, 12, 48]
sum = []
def myFun(list1, list2, sum):
    shorterList, longerList = [], []
    if len(list1) <= len(list2):
        shorterList, longerList = list1, list2
    else:
        shorterList, longerList = list2, list1
    for i in range(len(shorterList)):
        sum.append(shorterList[i] + longerList[i])
    for i in range(len(shorterList), len(longerList)):
        sum.append(longerList[i])

myFun(list1, list2, sum)
print(sum)