6. 函数
函数是代码的组织形式,通过使用函数,程序编写、阅读、测试和修复起来都更加容易。
带名字的代码块,目的是重复使用
函数是带名字的代码块,如果需要多次执行相同功能,只需要调用函数即可,无需重复写代码。在 Python 中,函数是一等对象。编程语言理论家把“一等对象”定义为满足下述条件的程序实体:
- 在运行时创建
- 能赋值给变量或数据结构中的元素
- 能作为参数传给函数
- 能作为函数的返回结果
函数是 Python 内建支持的一种封装,我们通过把大段代码拆成函数,通过一层一层的函数调用,就可以把复杂任务分解成简单的任务,这种分解可以称之为面向过程的程序设计。
6.1 定义函数
# 没有运行参数,没有返回值(返回一个None)
def greeting():
"""显示简单的问候 —— 方法中的第一行字符串,可以当做方法的文档"""
print('hello world')
greeting() # 调用该方法
以 # 号开头进行注释则不会显示方法的文档
6.1.1 向函数传递参数
def greeting2(user_name):
print(f'hello {user_name}')
greeting2('dream')
greeting2(user_name="xunmi")
6.1.2 形参和实参
函数定义中的参数叫形参,函数调用时传递的具体参数的值叫实参
def information(subject): # 这里的subject为形参
print(f'我学了{subject}课程')
subjects = ['Python', 'JAVA', 'excel', 'C++']
for subject in subjects: # 这里的subject为实参
information(subject)
我学了Python课程
我学了JAVA课程
我学了excel课程
我学了C++课程
6.2 传递参数
6.2.1 位置参数
调用的时候严格按照参数的位置顺序
def desc_pet(pet_type, pet_name):
"""显示宠物信息,参数:宠物物种,宠物名字"""
print(f'我有一只{pet_type},它的名字是{pet_name}')
desc_pet('狗', '钢镚')
我有一只狗,它的名字是钢镚
6.2.2 关键字参数
直接使用形参的名字
desc_pet(pet_name = '钢镚', pet_type = '狗')
6.2.3 默认值
默认值放在位置参数的后面
def desc_pet(pet_name, pet_type = 'dog'):
"""显示宠物信息,参数:宠物物种,宠物名字"""
print(f'我有一只{pet_type},它的名字是{pet_name}')
desc_pet('钢镚')
我有一只dog,它的名字是钢镚
desc_pet('钢镚', pet_type='乌龟') # 覆盖默认值
6.3 返回值
return,# 如无return 则无法在函数以外运用得到的值,使用返回值时会返回None
6.3.1 返回简单值
def get_full_name(first_name, last_name):
"""返回完整的名字"""
full_name = first_name + ' ' + last_name
return full_name # 如无return <value> 则无法在函数以外运用得到的值,使用返回值时会返回None
full_name = get_full_name('lizz', 'Truss')
print(full_name)
lizz Truss
如无 return full_name
则输出为:None
6.3.2 返回字典
def build_person(first_name, last_name, age=None):
"""返回一个字典,包含某人的信息"""
person = {
'first': first_name.title(),
'last': last_name.title()
}
if age:
person['age'] = age
return person
someone = build_person('john', 'snow', 28)
print(someone)
{'first': 'John', 'last': 'Snow', 'age': 28}
practice
# 通过输入得到人员姓名,并且输入Q时退出程序 (使用上方<build_person>方法)
while True:
first_name = input('请告诉我你的First Name:')
if first_name == 'Q':
print('结束')
break
else:
last_name = input('请告诉我你的Last Name:')
if last_name == 'Q':
print('结束')
break
else:
full_name = build_person(first_name, last_name)
print(full_name)
代码精简后
while True:
first_name = input('请告诉我你的First Name:')
if first_name == 'Q':
break
last_name = input('请告诉我你的Last Name:')
if last_name == 'Q':
break
full_name = build_person(first_name, last_name)
print(full_name)
print('\n退出循环')
6.4 传递任意数量的参数
在形参名字前面加一个
*
,在函数内部,输入的参数放在同一个元组中,名字就是形参的名字
def sum(*args):
_sum = 0
for num in args:
_sum += num
return _sum
_sum = sum(1, 2, 3, -3)
print(_sum)
6.4.1 可变参数的位置
def make_pizza(*stuffs, size='9寸'): # 可变参数放在位置参数的后面
"""打印披萨的所有配料"""
print(f'披萨的尺寸为{size}')
for stuff in stuffs:
print(stuff)
make_pizza('蘑菇', '芝士', size='8寸') # 如果包含了可变参数和默认参数,默认参数要放在后面
6.4.2 可变数量的关键字参数
形参名字前加
**
,在函数内部当作字典使用
def user_information(**user_profile):
return user_profile
print(user_information(name = '王大锤', age =20 ))
6.4.3特殊参数
通常情况下参数可以按位置或显式关键字传递给 Python 函数。
为了让代码易读、高效,最好限制参数的传递方式,这样开发者只需查看函数定义,即可确定参数项是 仅按位置、按位置或关键字,还是仅按关键字传递。完整函数定义如下:
/ 和 * 是可选的。这些符号表明形参如何把参数值传递给函数:位置、位置或关键字、关键字
- 函数定义中未使用 / 和 * 时,参数可以按位置或关键字传递给函数。
- 仅限位置形参应放在 / (正斜杠)前
- 仅限关键字形参应放在 * 后
参数定义的顺序必须是:位置参数、默认参数、可变参数(元组、字典)
def f(a, b, c=0, *args, **kw):
6.5 递归函数
在函数内部可以调用其他函数。如果一个函数在内部调用自身,这个函数就是递归函数。
def recur_fibo(n):
"""递归函数输出斐波那契数列<后一个数字为前两个数字之和>"""
if n <= 2:
return n
else:
return(recur_fibo(n-1) + recur_fibo(n-2))
print(recur_fibo(5)) # 值为8
6.6 高阶函数
6.6.1 高阶函数的概念
函数里的参数可以是函数,返回值也可以是函数,甚至函数还可以赋值给其他变量
z = abs # 一个变量可以指向函数
def add(x, y, z): # 第三个变量为一个函数
return z(x) + z(y)
def foo(x):
return 10*abs(x)
print(add(-5, 7, foo)) # 通过函数套用的方式执行代码(三层函数套用 —— z,add,foo)
120
6.6.2 匿名函数
在python中,匿名函数,顾名思义就是没有名字的函数,它主要用在那些只使用一次的场景中。这种场景下使用匿名函数往往能够让你的程序更加简单。匿名函数不需要显示地定义函数名,关键字
lambda
表示匿名函数,因此匿名函数也叫lambd
函数:
lambda [arg1 [,arg2, ... argN]] : expression
lambda 式子中冒号前面的是函数参数, 冒号后面的是函数的返回值。
注意: lambda 函数必须为单行,且不能包含 return 语句,返回值就是该表达式的结果。
# 常规函数 ,求两个数的和
def adder(x, y):
return x + y
#改写成匿名函数
lambda x,y:x+y
用匿名函数有个好处,因为函数没有名字,不必担心函数名冲突。此外,匿名函数也是一个函数对象, 也可以把匿名函数赋值给一个变量,再利用变量来调用该函数:
# 赋值给变量
adder = lambda x, y: x + y
print(adder(2, 5))
6.6.3 Python内置高阶函数
1. map
map() 函数接收两个参数,一个是函数,一个是可迭代集合, map将传入的函数依次作用到集合的每个 元素,并把结果作为新的可迭代集合返回
numbers = [i**2 for i in list(range(10))]
print(numbers)
def square(x):
return x**2
print(list(map(square, numbers)))
print(list(map(lambda x: x**2, numbers))) # 使用匿名函数一行代码即可实现
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
[0, 1, 16, 81, 256, 625, 1296, 2401, 4096, 6561]
[0, 1, 16, 81, 256, 625, 1296, 2401, 4096, 6561]
2. reduce()
reduce 不是内置函数,使用前需要导入;而map是内置函数,可以直接使用
from functools import reduce # 导入
与map类似, reduce 函数接收两个参数,一个是函数,一个是可迭代集合。 reduce () 把一个函数作用 在一个序列[x1, x2, x3, …]上,这个函数必须接收两个参数, reduce把结果继续和序列的下一个元素做函 数运算,最终输出单一值。完整语法是:
reduce(function, iterable, [initializer])
# 使用reduce计算1-100的和
from functools import reduce # 导入
numbers = list(range(1, 101))
def add(x, y):
return x+y
print(add, numbers) # 或者定义匿名函数实现
print(reduce(lambda x, y: x+y, numbers))
# reduce方法实现将整数列表变成连在一起的整数(方法1)
from functools import reduce # 导入
numbers = [1, 3, 4, 1]
def add(x, y):
return str(x)+str(y)
print(int(reduce(add, numbers)))
# reduce方法实现将整数列表变成连在一起的整数(方法2:使用不同函数进行处理)
from functools import reduce # 导入
numbers = [1, 3, 4, 1]
def add(x, y):
return x*10 + y
print(reduce(add, numbers))
1341
#计算10的阶乘
from functools import reduce # 导入
def factorial(x, y): # 方法1
return x * y
print(reduce(factorial, list(range(1, 11))))
i = 1 # 方法2
q = 1
while i <= 10:
q = i * q
i += 1
print(q)
print(reduce(lambda x, y: x * y, list(range(1, 11)))) # 方法3
3628800
3. filter
filter() 函数接收两个参数,一个是函数,一个是可迭代集合。对集合中的每一项依次执行函数,将执行结果为True的元素保留,为False的元素则删除,以此来对集合进行过滤。
# 保留负数
num_list = [1, -1, -2, 0, 2, 3, 6]
def fn(x):
return x < 0
print(list(filter(fn, num_list)))
6.7 将函数保存到模块
6.7.1 导入模块
创建自己的模块时,要注意:
- 模块名要遵循Python变量命名规范,不要使用中文、特殊字符;
- 模块名不要和系统模块名冲突,最好先查看系统是否已存在该模块。
下面是一个建议的模块格式:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
""" 第一个字符串是模块说明。下面是一些文档元素据,作者、版权、修订历史之类 """
__author__ = 'wincheer yang'
# 导入必要的模块
import sys
# 函数
def foo():
pass
def my_func2(name):
print('你好 % s' % name)
# 入口函数
if __name__ == '__main__':
print('这里是本模块的入口处。命令行 python 本模块名 .py 将执行这里的代码')
导入模块的方法有多种:
# 导入全部函数
import first_module
first_module.foo() # 调用函数 - 模块名 .函数(参数)
# 导入指定函数
from first_module from first_module from first_module foo() # 调用函数 -
import foo # 导入指定函数
import foo,bar # 导入多个函数
import * # 导入全部函数
# 函数(参数) ,无需指定模块名
# 给函数指定别名
from first_module import foo as fn
fn() # 调用函数 - 函数别名(参数) ,无需指定模块名
# 给模块指定别名
import first_module as m
m.foo() # 调用函数 - 模块别名 .函数(参数)
6.7.2 包
Python 2.x 的规定,每一个包目录下面都会有一个__init__.py的文件,否则Python就把这个目录当成普通目录而不是一个包。而在 Python 3.x 中,init.py 对包来说并不是必须的。
from com.wlw.pythom.hello import wlw #from <文件位置> import <模块名称>
项目结构示例:
- project/ :项目模块顶层包,通常顶层包下按功能组织模块
- docs/:存放一些项目文档。
- setup.py: 安装、部署、打包的脚本。
- README.md 能简要描述该项目的信息,让读者快速了解这个项目。
6.7.3 模块搜索路径添加
默认情况下, Python解释器会搜索当前项目目录、所有已安装的内置模块和第三方模块。搜索路径存放在 sys模块的path变量中:
import sys
sys.path
如果我们要添加自己的搜索目录,有两种方式:
- 直接修改sys.path,添加要搜索的目录:
import sys
sys.path.append(r'D:\wincheer\python-tour') #运行结束后失效
- 第二种方法是设置环境变量 PYTHONPATH ,该环境变量的内容会被自动添加到模块搜索路径中。设 置方式与设置Path环境变量类似。注意只需要添加你自己的搜索路径, Python自己本身的搜索路径不受影响。