注:该学习笔记是根据曾志贤老师编写的《从Excel到Python,用Python轻松处理Excel数据》所学习整理的笔记。
第九章 自定义函数
一、自定义函数编写规范
1、函数的定义
- 语法结构:
- def 函数名(参数):
- 函数体
- return 返回值
- 说明:
- def:函数的关键字,不能没有,也不能改,此关键字表示自定义函数。
- 函数名:函数的名称,根据函数名调用函数。不能一数字开头,尽量定义成可以表示函数功能的名称。
- 参数:为函数体提供数据。参数是被放在小括号中的,可以设置多个参数,参数之间用逗号分隔。在小括号后的后面加冒号。
- return 返回值:整体作为自定义函数的可选参数,用于设置自定义函数的返回值,也就是说自定义函数可能有返回值,也可能没有返回值,视情况而定。
注意:函数体和返回值语句要做缩进处理,也就是从冒号开始换行的语句都要做缩进处理。
- def 函数名(参数):
2、自定义函数的创建与调用
- 在自定义函数时,函数中的参数被称为形参,也就是形式上的参数,用于接收传递过来的数据。
- 在调用自定义参数时,向函数的参数写入的值被叫做实参,也就是实际上的参数。
# 有返回值的自定义函数
def total_sum1(price, amount):
money = price * amount
return money
# 返回200
print(total_sum1(10, 20))
# 无返回值的自定义函数
def total_sum2(price, amount):
str = '单价:{} 数量:{} 金额:{}'.format(price, amount, price * amount)
print(str)
# 返回单价:10 数量:20 金额:200
total_sum2(10, 20)
案例一、平均函数的定义及应用
- 将每个人1~12月的工资进行平均,然后写入N列对应的单元格。
# 自定义平均函数
def average(lst):
num = sum(lst) / len(lst)
avg = float('{:.2f}'.format(num))
return avg
# 数据处理
import xlrd
from xlutils.copy import copy
wb = xlrd.open_workbook('9-2.xls')
ws = wb.sheet_by_name('工资表')
nwb = copy(wb)
nws = nwb.get_sheet('工资表')
for row_num in range(1, ws.nrows):
nws.write(row_num, ws.ncols - 1, average(ws.row_values(row_num)[1:-1]))
nwb.save('9-2-1.xls')
二、必选参数的写法及应用
1、必选参数(位置参数)
必选参数就是函数调用时必须要传入值的参数,既不能多,也不能少。必选参数也可以叫作位置参数。
# 自定义函数
def level(number, lv1, lv2, lv3):
# number、lv1、lv2、lv3 必选参数
if number >= 90:
return lv1 #
elif number >= 60:
return lv2
elif number >= 0:
return lv3
# 调用1
for score in [95, 63, 58, 69, 41, 88, 96]:
print(score, level(score, '优', '良', '差'))
案例一、给号码分段
给工作表B列按照每4位分一段,如果不够4位,也要分成一段。
# 自定义函数
def intercept(s, num, delimiter):
# s:将数据转换为字符串,num:将要分几段,delimiter:分割使用的符号
s1 = str(s)
lst = []
# 循环字符串长度,步长为num
for n in range(0, len(s1), num):
# 截取字符串num的长度,放入列表
lst.append(s1[n:n + num])
# 将分割符号与列表进行重新组合
s2 = delimiter.join(lst)
# 将结果返回给函数
return s2
# 使用自定义函数
import xlrd
from xlutils.copy import copy
wb = xlrd.open_workbook('9-4.xls')
ws = wb.sheet_by_name('卡号表')
nwb = copy(wb)
nws = nwb.get_sheet('卡号表')
for row_num in range(1, ws.nrows):
val = ws.cell_value(row_num, 1)
nws.write(row_num, ws.ncols - 1, intercept(val, 4, '-'))
nwb.save('9-4-1.xls')
三、可选参数的写法及应用
1、可选参数(默认参数)
- 可选参数表示在调用函数时可传可不传该参数的值。
- 在定义可选参数时,要设置一个值,这个值叫作默认值,所以可选参数也叫作默认参数。
- 在调用函数时,如果可选参数没有传入值,则使用可选参数的默认值。
- 在自定义函数时,既有必选参数,又有可选参数,要先定义必选参数,在定义可选参数。在调用函数时也一样,要先传必选参数,在传可选参数。
# 自定义函数
def mid(iterable, start=0, lenght=1):
# 设置可选参数默认值的方式是:参数=值,不传入参数时,使用默认值
return iterable[start:start+lenght]
print(mid('abcdefgh'))
print(mid('abcdefgh', 2))
print(mid('abcdefgh', 2, 5))
案例一、模拟Vlookup函数的应用
按照“销售表”中A列的产品名,到“单价表”中查询对应的产品价格,将合计的价格写入“销售表”中。
# 自定义函数
def vlookup(val, lst, num=1):
# 产品名、所在的列表、返回第几列
# 在lst第0列,查找产品名,返回它所在的位置
r = lst[0].index(val)
# 返回lst第num列,的第对应位置的值
v = lst[num][r]
return v
# 引用自定义函数
import xlrd
from xlutils.copy import copy
wb = xlrd.open_workbook('9-6.xls')
ws1 = wb.sheet_by_name('单价表')
ws2 = wb.sheet_by_name('销售表')
nwb = copy(wb)
nws = nwb.get_sheet('销售表')
# 将单价表放入列表
lst = [ws1.col_values(0)[1:], ws1.col_values(1)[1:]]
for row_num in range(1, ws2.nrows):
# 提取行的所有值
vals = ws2.row_values(row_num)[:-1]
# 进行自定义函数的查询,并且计算
val = vlookup(vals[0], lst) * vals[1]
nws.write(row_num, ws2.ncols - 1, val)
nwb.save('9-6-1.xls')
四、关键字参数的写法及应用
在函数调用时,通过“参数=值”的形式传入实参的方式叫作关键字参数,可以让函数更加清晰、易于使用,同时参数的书写可以不按顺序。
1、关键字参数
- 关键字参数分为:
- 普通关键字参数:可以用“参数=值”的方式传入实参也可以按参数位置的顺序直接传入实参。
- 命名关键字参数:必须用“参数=值”的方式传入实参。
# 普通关键字参数
def counter1(iterable, min, max):
lst = [v for v in iterable if v>=min and v<=max]
return lst
# 按位置传递实参,必须按照顺序
print(counter1([2, 3, 8, 3, 4, 5, 9], 3, 8))
# 按关键字参数传递实参,可以不按照顺序
print(counter1(max=8, min=3, iterable=[2, 3, 8, 3, 4, 5, 9]))
# 命名关键字参数,*号以后得关键字,必须写为“参数=值”的格式
def counter2(iterable, *, min, max):
lst = [v for v in iterable if v>=min and v<=max]
return lst
# 按位置传递实参,必须按照顺序,且必须写命名参数
print(counter2([2, 3, 8, 3, 4, 5, 9], min=3, max=8))
# 如果不按照顺序传递参数,则都必须要命名参数
print(counter2(max=8, min=3, iterable=[2, 3, 8, 3, 4, 5, 9]))
案例一、分类合并字符串
以“工资表”工作表的“部门”列为基准,合并“姓名”列。
# 自定义函数
def combine(range, join_range, delimiter):
# 参数:作为key的列号、作为值的列号、连接符号
dic = {}
lst = []
# 将选择的两列使用zip重新组合,并放入循环
for x, y in zip(range, join_range):
# 判断字典是否存在key,并且放入对应的值
if not x in dic:
dic[x] = {y}
else:
dic[x].update({y})
# 循环字典,重新组合值,并且放入列表
for key, val in dic.items():
row_val = [key, delimiter.join(val)]
lst.append(row_val)
return lst
# 引用自定义函数
import xlrd
from xlutils.copy import copy
wb = xlrd.open_workbook('9-8.xls')
ws1 = wb.sheet_by_name('工资表')
ws2 = wb.sheet_by_name('结果表')
nwb = copy(wb)
nws = nwb.get_sheet('结果表')
row_num = 0
# 循环自定义列表,将值写入对应单元格
for x, y in combine(ws1.col_values(0), ws1.col_values(2), '、'):
nws.write(row_num, 0, x)
nws.write(row_num, 1, y)
row_num += 1
nwb.save('9-8-1.xls')
五、不定长参数的写法及应用
不定长参数是指定义的函数参数个数不确定,可以将不定长参数收纳在指定的容器中。
- 不定长参数分为一下两种,*args和**kwargs
- args与kwargs变量名称不是固定的,只是约定俗成的一种命名方式,用户可以修改成其他名称。不定长参数必须放在必选参数之后。
- *args和**kwargs接收的数据类型不限,完全根据需求而定。
1、不定长参数 *args
*args:以一个星号开头,然后接args变量,这种参数接收的值存储在元组中,即args是一个元组类型。
# *args 以元组的形式储存不确定的个数的参数
def subtotal(iterable, *args):
lst = []
for fun in args:
lst.append(fun(iterable))
return lst
# 第一组为必选参数,后面为添加的不定长参数
print(subtotal([7,8,9,10],max,min,sum))
# 返回 最大值:10、 最小值:7、和:34
2、不定长参数 **kwargs
**kwargs:以两个星号开头,然后接kwargs变量,这种参数接收的值存储在字典中,即kwargs是一个字典类型。
# **kwargs 以字段的形式储存不确定的个数的参数
def subtotals(iterable, **kwargs):
lst = []
for key, val in kwargs.items():
lst.append([key, val(iterable)])
return lst
# 第一组为必选参数,后面为添加的不定长参数,由于是字典形式,后面的参数相当于字典的key=val
print(subtotals([7, 8, 9,10], 求和=sum, 最大=max, 最小=min, 计数=len))
# 返回 [['求和', 34], ['最大', 10], ['最小', 7], ['计数', 4]]
案例一、替换函数增强版
将“书单表”工作表汇总的B列书名使用书名号进行分割。
# 自定义函数
def replaces(string, new, *old):
for o in old:
# 使用字符串替换函数进行替换,并且赋值给自己
string = string.replace(o, new)
return string
# 引用自定义函数
import xlrd
from xlutils.copy import copy
wb = xlrd.open_workbook('9-10.xls')
ws = wb.sheet_by_name('书单表')
nwb = copy(wb)
nws = nwb.get_sheet('书单表')
for row_num in range(1,ws.nrows):
# 这里需要提取成字符串,而不是列表,所以至取第1列
vals = ws.row_values(row_num)[1]
nws.write(row_num, ws.ncols - 1, '《' + replaces(vals, '》《', ' ', '|') + '》')
nwb.save('9-10-1.xls')
六、匿名函数的写法及应用
- 匿名函数指无需定义标识符的函数或者子程序。
- python中用lambda关键字定义匿名函数,只用表达式而无需声明。
- 使用匿名函数的好处:匿名函数没有名字,不必担心函数名冲突。
- 匿名函数也是一个函数对象,可以把匿名函数赋值给一个变量,这样变量名就相当于函数名,可以利用变量来调用该函数。
1、匿名函数的语法结构
- lambda与def的区别:
- lambda是表达式,只能封装有限的业务逻辑,适用于编写简单的函数。
- def是语句块,适用于处理更大量的业务。
lambda语法结构:
fun = lambda[参数]:expression
#变量 = lambda[参数]:处理表达式及返回值
2、匿名函数的常见书写方式
# 将匿名参数赋值给变量
fun1 = lambda x: x*10
print(fun1(20))
# 必选参数
fun2 = lambda x, y: x+y
print(fun2(10, 20))
# 可选参数
fun3 = lambda x, y=30: x+y
print(fun3(10))
# 不定长参数*args
fun4 = lambda x, *args: [f(x) for f in args]
print(fun4([3, 4, 5, 6], sum, max, min, len))
# 不定长参数**kwargs
fun5 = lambda x, **kwargs: [key + ':' + str(val(x)) for key, val in kwargs.items()]
print(fun5([3, 4, 5, 6], 求和=sum, 最大=max, 最小=min, 数量=len))
案例一、根据身份证号判断性别
将“身份证表”中的A列身份证号的第17位数字判断性别,奇数为男性,偶数为女性。然后写入B列。
import xlrd
from xlutils.copy import copy
wb = xlrd.open_workbook('9-12.xls')
ws = wb.sheet_by_name('身份证表')
nwb = copy(wb)
nws = nwb.get_sheet('身份证表')
for row_num in range(1, ws.nrows):
# 列表推导式,整型身份证号的倒数第二位,整除2的余数判断男女
card = lambda id: '男' if int(id[-2]) % 2 == 1 else '女'
val = ws.cell_value(row_num, 0)
nws.write(row_num, ws.ncols - 1, card(val))
nwb.save('9-12-1.xls')
七、自定义函数存放在.py文件中
自定义函数可以被单独存放在一个.py文件中,这样其他.py文件也可以调用,实现自定义函数的复用。
1、函数定义在单独.py文件中
# 引用指定模块(会引用所有函数)
import 模块
# 引用指定模块所有的函数
from 模块 import *
# 引用指定模块下的指定单个或多个函数
from 模块 import 函数1,函数2
2、函数定义在文件夹中
如果模块较多的话,可以将多个模块放入包中(文件夹)
# 引用包中的所有模块
from 包名称 import *
# 引用包中的指定模块的指定函数
from 包名称.模块 import 函数