目录
1.自定义函数
1.1Pycharm相关设置
1.2定义
函数内存图
1.3python作用
1.4定义函数
1.5调用函数
1.6返回值
1.7可变/不可变类型在传参
1.8函数参数
1.8.1实参传递方式argument
1.8.2形参定义方式parameter
1.9作用域LEGB
1.9.1变量名的查找规则
1.9.2局部变量
1.9.3全局变量
1.9.4 global语句
1.9.4 nonlocal语句
练习
def attack(): # 定义函数(做功能),使用功能的话要调用
print("摆拳")
print("直拳")
print("肘击")
print()
# 调用函数
attack()
attack()
attack()
def attack(): # 定义函数(做功能),使用功能的话要调用
print("摆拳")
print("直拳")
print("肘击")
print()
def attack_repeated(count): # 形式参数
for i in range(count): # 次数不能写死了
print("摆拳")
print("直拳")
print("肘击")
print("临门一脚")
print()
attack_repeated(2) # 调用函数的时候(2)是实际参数
F8 -->逐过程调试(调试时跳过方法);F7 -->逐语句调试(调试时进入方法)
“代码自动完成”时间延时设置
File -> Setting->Editor->General->Code com
快捷键:
Ctrl + P:参数信息(在方法中调用参数)
Ctrl + Q:快速查看文档
Ctrl + Akt + M:提取方法
函数用于封装一个特定的功能,表示一个功能或者行为。
函数是可以重复执行的语句块,可以重复调用。
注意:自定义函数的调试快捷键是F7(逐语句),其中F8是逐过程。
# 在方法区中存储函数代码,不执行函数体
def fun01(a):
a = 100
num01 = 1
fun01(num01) # 因为调用函数,所以开辟一块内存图,叫做栈帧,由于存储在函数内部定义的变量
# 函数执行完毕后,栈帧立即释放(其中定义的变量也会销毁)
print(num01) # 1
def fun02(a):
a[0] = 100
# 改变的是传入的可变对象
list01 = [1]
fun02(list01)
print(list01[0])
def fun03(a):
a = 100
# 改变的是fun03栈帧中变量a的指向
list01 = [1]
fun03(list01)
print(list01[0]) # 1
def fun04(a):
a[1] = [200]
b = 10
list01 = [1, [2, 3]]
fun04(list01)
print(list01[1]) # [200]
g01 = "ok"
def fun05():
l01 = 100
global g01
g01 = "no"
global g02
g02 = 250
fun05()
print(g01) # no
print(g02) # 250
函数可以提高代码的可重用性和可维护性(代码层次结构更清晰)。
语法
def 函数名(形式参数):
函数体
说明:
def关键字:全称是“define”,意为“定义”
函数名:对函数体中语句的描述(见名知意),规则与变量名要求相同。
形式参数:方法定义者要求调用者提供的信息。
函数体:完成该功能的语句。
函数的第一行语句建议使用文档字符串描述函数的功能
def attack_repeated(count): # 形式参数
"""
单次攻击
:param count:攻击次数
:return:
"""
for i in range(count): # 次数不能写死了
print("摆拳")
print("直拳")
print("肘击")
print("临门一脚")
print()
语法:函数名(实际参数)
说明:根据形式参数传递内容
定义:方法定义者告诉调用者的结果
语法:
return 结果
说明:
return后没有语句,相当于返回None;
函数体没有return,相当于返回None。
通过元组向外返回多个结果(返回多个结果时默认是元组)
(结合1.2中的函数内存图看)
不可变类型参数有:
数值型(整数、浮点数、复数)
布尔值bool
None空值
字符串str
元组tuple
固定集合frozenset
可变类型参数有:
列表list
字典dict
集合set
传参说明:
不可变类型的数据在传参时,函数内部不会改变原数据的值。
可变类型的数据在传参时,函数内部可以改变原数据。(可以不是一定)
函数参数包括实际参数和形式参数。
def fun01(a, b, c, d):
print(a)
print(b)
print(c)
print(d)
# 位置实参:实参和形参根据位置依次对应
# fun01(1, 2, 3, 4)
# 关键字实参:实参和形参根据名称进行对应
# fun01(b=1, d=2, c=3, a=4)
# 序列实参:星号将序列拆分后按位置与形参进行对应
# 序列实参属于位置实参
# 如果参数很多,可以存储在序列(字符串/列表/元组)中,再通过*拆分直接传入函数
list01 = ['a', 'b', 'c', 'd']
# fun01(list01) # 报错,缺3个位置实参
fun01(*list01)
# 字典实参:双星号将字典拆分后按名称与形参进行对应
# 如果参数很多可以存储在字典中,再通过**拆分,传入函数
dict01 = {"a":1,"c":3,"d":4,"b":2}
# fun01(dict01) # 报错,缺3个位置实参
fun01(**dict01)
# 缺省(默认)参数:如果实参不提供,可以使用默认值(允许实参不提供)
def fun01(a=0, b=0, c=0, d=0):
print(a)
print(b)
print(c)
print(d)
fun01(b=2, c=3) # 0 2 3 0
fun01(2,3) # 2 3 0 0
# 关键字实参+缺省实参:调用者可以随意传递参数
# 练习:根据小时、分钟、秒计算总秒数
def time(hour=0, min=0, sec=0):
return hour * 60 * 60 + min * 60 + sec
a = time(min=1, sec=52)
print(a)
# 星号元组形参:*将所有实参合并为一个元组
# 作用:让实参个数无限制(最好写作args)
def fun01(*args):
print(args)
fun01() # ()
fun01(1) # (1,)
fun01(1, 2, 3) # (1, 2, 3)
fun01(1, '2', 3) # (1, '2', 3)
# 练习:定义数值相加的函数
def add(*args):
result = 0
for item in args:
result += item
return result
print(add(1, 2, 3, 4, 5))
# 命名关键字形参:在星号元组形参以后的位置形参
# 目的:要求实参必须使用关键字实参
def fun01(a, *args, b):
print(a)
print(args)
print(b)
fun01(1, b=2)
print()
fun01(1, 2, 3, 4, 5, 6, b=2)
print()
def fun02(*, a, b):
print(a)
print(b)
print(fun02(a=1, b=2))
# 双星号字典形参:实参可以传递数量无限的关键字实参
# **目的是将实参合并为字典
def fun01(**kwargs):
print(kwargs)
fun01(a=1, b=2)
# {'a': 1, 'b': 2}
参数自左至右的顺序:
位置形参 -> 星号元组形参 -> 命名关键字形参 -> 双星号字典形参
def fun01(a, b, *args, c, d, **kwargs):
pass
fun01(1,2,c=3, d=4)
作用域:变量起作用的范围
local局部作用域:函数内部(局部作用域定义的变量称为局部变量)
enclosing外部嵌套作用域:函数嵌套
global全局作用域:模块(.py文件)内部(全局作用域定义的变量称为全局变量)
builtin内置模块作用域:builtins.py文件(如查看print,点在print然后Ctrl+鼠标左键)需注意变量名不与关键字重复
# 全局变量
g01 = "ok"
def fun01():
l01 = 100 # 局部变量
print(l01) # 100
g01 = "no" # 创建了一个局部变量g01而不是修改全局变量
print(g01) # no
fun01()
print(g01) # ok
"""
在函数内部可以读取全局变量,如果在函数内部修改全局变量则相当于在函数内部创建了一个局部变量,原先的全局变量并没有被修改(如果一定要在函数内部修改全局变量则需要用global)
"""
# ----------------------------------------------------------
# 全局变量
g01 = "ok"
def fun01():
l01 = 100 # 局部变量
print(l01) # 100
global g01 # 定义全局变量g01
g01 = "no" # 此时修改的是全局变量
print(g01) # no
fun01()
print(g01) # no
由内而外:L -> E -> G -> B
在访问变量时,先查找本地变量,然后是包裹此函数的变量,再是全局变量,最后是内置变量
定义在函数内部的变量(形参也是局部变量)
只能在函数内部使用
调用函数时才被创建,函数结束后自动销毁
定义在函数外部,模块内部的变量
在整个模块(py文件)范围内访问(但函数内不能将其直接赋值)
作用:
在函数内部修改全局变量
在函数内部定义全局变量(全局声明)
语法:
global 变量1,变量2
说明
在函数内直接为全局变量赋值,视为创建新的局部变量
不能先声明局部的变量,再用global声明为全局变量
1.将下列代码定义到函数中,再调用一次。
for r in range(3):
for c in range(4):
print("*",end=" ")
print()
def rectangle(row, column):
"""
矩形绘图
:param row: 行
:param column: 列
:return:
"""
for r in range(row):
for c in range(column):
print("*", end=" ")
print()
rectangle(2, 5)
2.定义在控制台中打印一维列表的函数
例如:[1,2,3] --->(每个元素一行)
1
2
3
def print_list(list_target):
"""
打印一维列表
:param list_target: 目标列表
:return:
"""
for item in list_target:
print(item)
print_list([1, 2, 3])
3.在控制台中打印输出多维列表的函数
[
[1,2,3,4],
[4,5,5],
[7,59,2,1,3]
] ---->
1 2 3 4
4 5 5
7 59 2 1 3
# 自己做
def print_multidimensional_list(target_list):
"""
:param target_list: 要打印的列表
:return:
"""
for r in range(len(target_list)):
for item in target_list[r]:
print(item,end=" ")
print()
print_multidimensional_list([
[1,2,3,4],
[4,5,5],
[7,59,2,1,3]
])
# 老师讲解:
def print_double_list(double_list):
"""
打印二维列表
:param double_list: 需要打印的列表
:return:
"""
for line in double_list:
for item in line:
print(item, end=" ")
print()
list01 = [
[1, 2, 5, 88],
[4, 5, 8, 9, 7, 8, 88, 999, 666, 3, 8, 7],
[7, 5, 9687]
]
print_double_list(list01)
4.定义函数打印输出已知方形矩阵(n*n)的转置矩阵(思路:主对角矩阵元素不变,其他对称交换)
TIPS:外层循环执行一次,外层循环执行多次;外层循环控制行,内层循环控制列。
def transpose_matrix(target_matrix):
"""
:param target_matrix: 已知的矩阵
:return:
"""
for r in range(len(target_matrix) - 1):
for c in range(r + 1, len(target_matrix)):
target_matrix[r][c], target_matrix[c][r] =
target_matrix[c][r], target_matrix[r][c]
print(target_matrix)
transpose_matrix([
[1, 2, 3], [4, 5, 6], [7, 8, 9]
])
# 老师讲解思路
list01 = [
[1, 2, 3, 4],
[5, 6, 7, 8],
[9, 10, 11, 12],
[13, 14, 15, 16]
]
"""
思路:主对角元素不变
list01[1][0] <-->list01[0][1]
list01[2][0] <-->list01[0][2]
list01[3][0] <-->list01[0][3]
"""
for r in range(1, 4):
list01[r][0], list01[0][r] = list01[0][r], list01[r][0]
# -----------------------------------------------------
"""
list01[2][1] <-->list01[1][2]
list01[3][1] <-->list01[1][3]
"""
for r in range(2,4):
list01[r][1], list01[1][r] = list01[1][r], list01[r][1]
5.定义函数:根据两计算几斤零几两的函数
def get_weight_for_jin(liang_weight):
"""
根据两,计算几斤几两
:param liang_weight: 需要计算的两数
:return: 元组(斤,两)
"""
jin = liang_weight // 16
liang = liang_weight % 16
return (jin, liang)
6.自定义函数,通过分数计算分数等级
def get_score_grade(score):
"""
根据分数计算相应的成绩等级
:param score: 分数
:return: 等级(字符串)
"""
if score > 100 or score < 0:
return "输入有误"
elif score >= 90:
return "优秀"
elif score >= 80:
return "良好"
elif score >= 60:
return "优秀"
return "不及格"
7.定义函数:判断列表中是否存在相同元素(善用return减少嵌套层级,逻辑更为清晰)
两种可能结果返回布尔值(True/False)
def is_repeating(num_list):
"""
判断列表中是否存在相同元素
:param num_list: 已知的数字列表
:return:
"""
flag = False
for i in range(len(num_list) - 1):
for j in range(i + 1, len(num_list)):
if num_list[i] == num_list[j]:
return True # 有重复
return False # 没有重复
print(is_repeating([1, 2, 3, 4, 5]))
8.定义函数:通过月份输出天数(要求考虑闰年)
建议返回整型而不是字符串。不建议方法的返回值类型可能是多种
def month_to_day(year, month):
"""
通过年月判断有多少天
:param year: 已知年
:param month: 已知月
:return: 天数
"""
leap_year_day = (31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31,)
# 判断是否闰年
flag = year % 4 == 0 and year % 100 != 0 or year % 400 == 0
if month not in range(1, 13):
return "月份输入有误"
elif month == 2:
if flag:
return 29
return 28
return leap_year_day[month - 1]
print(month_to_day(1999, 2))
print(month_to_day(2000, 2))
# 老师讲解
def is_leap_year(year):
return year % 4 == 0 and year % 100 != 0 or year % 400 == 0
def get_day_by_month(year, month):
if month < 1 or month > 12:
return 0
if month == 2:
return 29 if is_leap_year(year) else 28
if month in (4, 6, 9, 11,):
return 30
return 31
print(get_day_by_month(1999, 2))
print(get_day_by_month(2000, 2))
注意:与主题无关的东西可以先提出去做一个函数,函数与函数之间可以相互调用,保证每个函数只做一件事。
9.定义列表升序排列的函数
注意:①传入的是可变对象,②函数体修改的是传入的对象,两个条件都满足则不需要通过返回值传递结果
def list_ascending(num_list):
"""
将列表升序排列
:param num_list: 已知某列表
:return: 升序排列的列表
传入的是可变对象,函数体修改的是传入的对象,不需要通过返回值传递结果
"""
for r in range(len(num_list) - 1):
for c in range(r + 1, len(num_list)):
if num_list[r] > num_list[c]:
num_list[r], num_list[c] = num_list[c], num_list[r]
num = [1, 4, 7, 2, 5, 8, 3, 6, 9]
list_ascending(num)
print(num)
10,定义方阵转置的函数
def transpose_matrix(list_target):
for r in range(len(list_target[0]) - 1):
for c in range(r + 1, len(list_target)):
list_target[r][c], list_target[c][r] = list_target[c][r], list_target[r][c]
num = [
[1, 2, 3, 4],
[5, 6, 7, 8],
[9, 10, 11, 12],
[13, 14, 15, 16]
]
transpose_matrix(num)
print(num)
# 老师讲解
def square_matrix_transpose(sqr_matrix):
"""
方阵转置
:param sqr_matrix: 二维列表类型的方阵
"""
for c in range(1, len(sqr_matrix)):
for r in range(c, len(sqr_matrix)):
sqr_matrix[r][c - 1], sqr_matrix[c - 1][r] = sqr_matrix[c - 1][r], sqr_matrix[r][c - 1]
num_list = [
[1, 2, 3, 4],
[5, 6, 7, 8],
[9, 10, 11, 12],
[13, 14, 15, 16]
]
square_matrix_transpose(num_list)
print(num_list)
11.记录函数 fun01的执行次数(借助全局变量在函数内部使用)
count = 0 # 计数器
def fun01():
global count
count += 1
fun01()
fun01()
fun01()
fun01()
text = "函数fun01一共调用{}次".format(count)
print(text)
12.完成字符串练习
msg = " 校 训 : 自 强不息、厚德载物。 "
# 1.查找空格的数量
count = msg.count(" ")
print("空格的数量为:%d" % count)
# 2.删除字符串前后空格
print(msg.strip())
# 3.删除字符串所有空格
print(msg.replace(" ",""))
# 4.查找"载物"的位置
print("载物的位置为:{}".format(msg.find("载物")))
# 5.判断字符串是否以"校训"开头
print(msg.startswith("校训"))
13.定义函数,计算指定范围内的素数
def prime_number(start_num, end_num):
"""
计算指定范围内的素数
:param start_num: 范围开始值,默认为0
:param end_num: 范围结束值(不包括结束值)
:return:
"""
result = []
for number in range(start_num, end_num):
for rem in range(2, number):
if number % rem == 0:
break
else:
result.append(number)
return result
print(prime_number(5, 30))
# 优化:把一个大问题分成若干个小问题
def is_prime(number):
"""
判断指定的数字是否是素数
:param number:
:return:True表示是素数,False表示不是素数
"""
for item in range(2, number):
if number % item == 0:
return False
return True
def get_prime(begin, end):
"""
生成指定范围的素数
:param begin:
:param end:
:return:
"""
return [number for number in range(begin, end) if is_prime(number)]
print(get_prime(5, 30))
注意:将代码提取成一个函数——Ctrl+Alt+M;选中后右键->重构->提取方法
14.讨论is和"=="的区别
is是地址相同,而"=="是值相同
15.玩2048游戏
# 1. 将某个已知列表中的0元素移到列表最后
# [2,0,2,0]->[2,2,0,0]
# [2,0,0,2]->[2,2,0,0]
# [2,4,0,2]->[2,4,2,0]
# 法1:判断是零则和右边元素交换(性能好)
def zero_to_end(list_merge):
for index in range(len(list_merge) - 1):
if list_merge[index] == 0:
list_merge[index], list_merge[index + 1] = list_merge[index + 1], list_merge[index]
# ---------------------------------------------------
# 法2:从右往左判断是零则删除并在末尾添加一个零(简单)
def zero_to_end(list_merge):
for index in range(len(list_merge) - 1, -1, -1):
if list_merge[index] == 0:
del list_merge[index]
list_merge.append(0)
# 2. 将相同数字进行合并
# [2,0,2,0]->[4,0,0,0]
# [2,0,0,2]->[4,0,0,0]
# [2,0,4,0]->[2,4,0,0]
# 法1:自己做
def merge(list_merge):
for i in range(len(list_merge) - 1):
for j in range(i + 1, len(list_merge)):
if list_merge[i] == list_merge[j]:
list_merge[i] *= 2
list_merge[j] = 0
zero_to_end(list_merge)
# ---------------------------------------------------------
# 法2:
def merge(list_merge):
"""
先将中间的零元素移植末尾,再合并相邻相同元素
"""
zero_to_end(list_merge)
for index in range(len(list_merge)-1):
if list_merge[index]==list_merge[index+1]:
list_merge[index]*=2
del list_merge[index+1]
list_merge.append(0)
"""
2048游戏核心算法
"""
list_merge = [0, 2, 2, 0]
# 1. 将某个已知列表中的0元素移到列表最后
# [2,0,2,0]->[2,2,0,0]
# [2,0,0,2]->[2,2,0,0]
# [2,4,0,2]->[2,4,2,0]
# 从右往左判断是零则删除并在末尾添加一个零(简单)
def zero_to_end():
"""
零元素移动到末尾
思想:从后往前,如果发现零元素,删除并追加
"""
for index in range(len(list_merge) - 1, -1, -1):
if list_merge[index] == 0:
del list_merge[index]
list_merge.append(0)
# 2. 将相同数字进行合并
# [2,0,2,0]->[4,0,0,0]
# [2,0,0,2]->[4,0,0,0]
# [2,0,4,0]->[2,4,0,0]
def merge():
"""
先将零元素移至末尾,再合并相等且相邻的元素
"""
zero_to_end()
for index in range(len(list_merge) - 1):
if list_merge[index] == list_merge[index + 1]:
list_merge[index] *= 2
del list_merge[index + 1]
list_merge.append(0)
# 3. 地图向左移动
map = [
[2, 0, 0, 2],
[4, 4, 2, 2],
[2, 4, 0, 4],
[0, 0, 2, 2]
]
def move_left():
"""
向左移动,核心思想:将二维列表中每行交给merge函数进行操作
:return:
"""
for line in map:
global list_merge
list_merge = line
merge()
def move_right():
"""
向右移动,核心思想:将二维列表中每行(从右往左)交给merge函数进行操作
"""
for line in map:
global list_merge
list_merge = line[::-1] # 从右往左取出数据形成新列表
merge()
line[::-1] = list_merge # 从右往左接收合并后的数据
# 4.向上移动 向下移动(矩阵转置后上下就变成了左右)
def square_matrix_transpose(sqr_matrix):
for r in range(len(sqr_matrix) - 1):
for c in range(r + 1, len(sqr_matrix)):
sqr_matrix[r][c], sqr_matrix[c][r] = sqr_matrix[c][r], sqr_matrix[r][c]
def move_up():
square_matrix_transpose(map)
move_left()
square_matrix_transpose(map)
def move_down():
square_matrix_transpose(map)
move_right()
square_matrix_transpose(map)
不谈细节不谈算法,谈思想:
不论是左移、右移、上移、下移,行与行、列与列之间的逻辑都是相似的,所以对于二维项目而言,由于行与列之间互不干扰,可以降维(上移等同于左移、下移等同于右移),将二维问题(上下左右)降为一维问题(左右)。2048的具体降维方法:左右为一维列表,上下则通过矩阵转置变为左右,仍是一维列表
16.完善 shopping.py 程序
dict_commodity_info = {
101: {"name": "屠龙刀", "price": 10000},
102: {"name": "倚天剑", "price": 10000},
103: {"name": "九阴白骨爪", "price": 8000},
104: {"name": "九阳神功", "price": 9000},
105: {"name": "降龙十八掌", "price": 8000},
106: {"name": "乾坤大挪移", "price": 10000},
}
list_order = []
def select_menu():
"""
选择菜单
:return:
"""
while True:
item = int(input("1键购买,2键结算:"))
if item == 1:
buying()
elif item == 2:
settlement()
def settlement():
total_price = calculate_total_price()
paying(total_price)
def paying(total_price):
"""
支付过程
:param total_price:
:return:
"""
while True:
money = float(input("总价{}元,请输入金额:".format(total_price)))
if money >= total_price:
print("购买成功,找回{}元".format(money - total_price))
list_order.clear()
break
else:
print("金额不足")
def calculate_total_price():
total_price = 0
for order in list_order:
commodity = dict_commodity_info[order["cid"]]
print("商品:{},单价:{},数量:{}".format(commodity["name"], commodity["price"], order["count"]))
total_price += commodity["price"] * order["count"]
return total_price
def buying():
"""
购买
:return:
"""
print_commodity_info()
creat_order()
print("添加到购物车")
def creat_order():
"""
创建订单
:return:
"""
cid = int(input("请输入商品编号:"))
count = int(input("请输入购买数量:"))
order = {"cid": cid, "count": count}
list_order.append(order)
def input_commodity_id():
"""
获取商品订单
:return:
"""
while True:
cid = int(input("请输入商品编号:"))
if cid in dict_commodity_info:
break
else:
print("该商品不存在")
return cid
def print_commodity_info():
"""
打印商品信息
:return:
"""
for key, value in dict_commodity_info.items():
print("编号:{},名称:{},单价:{}".format(key, value["name"], value["price"]))
select_menu()