目录
5.3 可以有多个return 语句,一旦其中一个执行,代表了函数运行的结束
一、概述
二、函数的定义及调用
1. 为什么要用函数
- 提高代码复用性——抽象出来,封装为函数
- 将复杂的大问题分解成一系列小问题,分而治之——模块化设计的思想
- 利于代码的维护和管理
顺序式
输入:
# 5的阶乘
n = 5
res = 1
for i in range(1, n+1):
res *= i
print(res)
# 20的阶乘
n = 20
res = 1
for i in range(1, n+1):
res *= i
print(res)
输出:
120
2432902008176640000
抽象成函数
输入:
def factoria(n):
res = 1
for i in range(1,n+1):
res *= i
return res
print(factoria(5))
print(factoria(20))
输出:
120
2432902008176640000
2. 函数的定义及调用
白箱子:输入——处理——输出
三要素:参数、函数体、返回值
2.1 定义
def 函数名(参数):
函数体
return 返回值
# 求正方形的面积
def area_of_square(length_of_side):
square_area = pow(length_of_side, 2)
return square_area
2.2 调用
函数名(参数)
输入:
area = area_of_square(5)
area
输出:
3. 参数传递
3.1 形参与实参
- 形参(形式参数):函数定义时的参数,实际上就是变量名
- 实参(实际参数):函数调用时的参数,实际上就是变量的值
3.2 位置参数
- 严格按照位置顺序,用实参对形参进行赋值(关联)
- 一般用在参数比较少的时候
输入:
def function(x, y, z):
print(x, y, z)
function(1, 2, 3) # x = 1; y = 2; z = 3
输出:
1 2 3
- 实参与形参个数必须一一对应,一个不能多,一个不能少
输入:
function(1, 2)
输出:
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-6-2a7da6ff9675> in <module>
----> 1 function(1, 2)
TypeError: function() missing 1 required positional argument: 'z'
输入:
function(1, 2, 3, 4)
输出:
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-8-748d3d0335e6> in <module>
----> 1 function(1, 2, 3, 4)
TypeError: function() takes 3 positional arguments but 4 were given
3.3 关键字参数
- 打破位置限制,直呼其名的进行值的传递(形参=实参)
- 必须遵守实参与形参数量上一一对应
- 多用在参数比较多的场合
输入:
def function(x, y, z):
print(x, y, z)
function(y=1, z=2, x=3) # x = 1; y = 2; z = 3
输出:
3 1 2
- 位置参数可以与关键字参数混合使用
- 但是,位置参数必须放在关键字参数前面
输入:
function(1, z=2, y=3)
输出:
1 3 2
输入:
function(1, 2, z=3)
输出:
1 2 3
- 不能为同一个形参重复传值
输入:
def function(x, y, z):
print(x, y, z)
function(1, z=2, x=3)
输出:
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-12-f385272db011> in <module>
3
4
----> 5 function(1, z=2, x=3)
TypeError: function() got multiple values for argument 'x'
3.4 默认参数
- 在定义阶段就给形参赋值——该形参的常用值
- 默认参数必须放在非默认参数后面
- 调用函数时,可以不对该形参传值
- 机器学习库中类的方法里非常常见
输入:
def register(name, age, sex="male"):
print(name, age, sex)
register("大杰仔", 18)
输出:
大杰仔 18 male
-
也可以按正常的形参进行传值
输入:
register("林志玲", 38, "female")
输出:
林志玲 38 female
-
默认参数应该设置为不可变类型(数字、字符串、元组)
输入:
def function(ls=[]):
print(id(ls))
ls.append(1)
print(id(ls))
print(ls)
function()
输出:
1759752744328
1759752744328
[1]
输入:
function()
输出:
1759752744328
1759752744328
[1, 1]
列表虽然地址未变,但是内容已经发生了变化,调用时根据地址追踪到内容。
输入:
function()
输出:
1759752744328
1759752744328
[1, 1, 1]
输入:
def function(ls="Python"):
print(id(ls))
ls += "3.7"
print(id(ls))
print(ls)
function()
输出:
1759701700656
1759754352240
Python3.7
ls 和 ls+“3.7”之后的地址发生了变化,调用时根据地址:1759701700656追踪到的是”Python”,所以不会出现和列表一样的记忆功能。
输入:
function()
输出:
1759701700656
1759754353328
Python3.7
输入:
function()
输出:
1759701700656
1759754354352
Python3.7
- 让参数变成可选的
输入:
def name(first_name, last_name, middle_name=None):
if middle_name:
return first_name+middle_name+last_name
else:
return first_name+last_name
print(name("大","仔"))
print(name("大", "仔", "杰"))
输出:
大仔
大杰仔
3.5 可变长参数*args
- 不知道会传过来多少参数 *args
- 该形参必须放在参数列表的最后
输入:
def foo(x, y, z, *args):
print(x, y ,z)
print(args)
foo(1, 2, 3, 4, 5, 6) # 多余的参数,打包传递给args
输出:
1 2 3
(4, 5, 6)
-
实参打散
输入:
def foo(x, y, z, *args):
print(x, y ,z)
print(args)
foo(1, 2, 3, [4, 5, 6])
输出:
1 2 3
([4, 5, 6],)
输入:
foo(1, 2, 3, *[4, 5, 6]) # 打散的是列表、字符串、元组或集合
“*”将列表、字符串、元组或集合打散。
输出:
1 2 3
(4, 5, 6)
3.6 可变长参数**kwargs
输入:
def foo(x, y, z, **kwargs):
print(x, y ,z)
print(kwargs)
foo(1, 2, 3, a=4, b=5, c=6) # 多余的参数,以字典的形式打包传递给kwargs
多余参数以字典的形式打包传递给kwargs
输出:
1 2 3
{'a': 4, 'b': 5, 'c': 6}
4. 函数体与变量作用域
- 函数体就是一段只在函数被调用时,才会执行的代码,代码构成与其他代码并无不同
- 局部变量——仅在函数体内定义和发挥作用
输入:
def multipy(x, y):
z = x*y
return z
multipy(2, 9)
print(z) # 函数执行完毕,局部变量z已经被释放掉了
输出:
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
<ipython-input-29-9a7fd4c4c0a9> in <module>
5
6 multipy(2, 9)
----> 7 print(z) # 函数执行完毕,局部变量z已经被释放掉了
NameError: name 'z' is not defined
- 全局变量——外部定义的都是全局变量
- 全局变量可以在函数体内直接被使用
输入:
n = 3
ls = [0]
def multipy(x, y):
z = n*x*y
ls.append(z)
return z
print(multipy(2, 9))
ls
函数调用完成后,z会被释放掉。
输出:
54
[0, 54]
- 通过global 在函数体内定义全局变量
输入:
def multipy(x, y):
global z
z = x*y
return z
print(multipy(2, 9))
print(z)
z在函数调用后不会被释放掉。
输出:
18
18
5. 返回值
5.1 单个返回值
输入:
def foo(x):
return x**2
res = foo(10)
res
输出:
100
5.2 多个返回值——以元组的形式
输入:
def foo(x):
return 1, x, x**2, x**3 # 逗号分开,打包返回
print(foo(3))
输出:
(1, 3, 9, 27)
输入:
a, b , c, d = foo(3) # 解包赋值
print(a)
print(b)
print(c)
print(d)
输出:
1
3
9
27
5.3 可以有多个return 语句,一旦其中一个执行,代表了函数运行的结束
输入:
def is_holiday(day):
if day in ["Sunday", "Saturday"]:
return "Is holiday"
else:
return "Not holiday"
print("啦啦啦德玛西亚,啦啦啦啦") # 你丫根本没机会运行。。。
print(is_holiday("Sunday"))
print(is_holiday("Monday"))
输出:
Is holiday
Not holiday
5.4 没有return语句,返回值为None
输入:
def foo():
print("我是孙悟空")
res = foo()
print(res)
输出:
我是孙悟空
None
6. 几点建议
1、函数及其参数的命名参照变量的命名
- 字母小写及下划线组合
- 有实际意义
2、应包含简要阐述函数功能的注释,注释紧跟函数定义后面
def foo():
# 这个函数的作用是为了给大家瞅一瞅,你瞅啥,瞅你咋地。。。。
pass
3、函数定义前后各空两行
def f1():
pass
# 空出两行,以示清白
def f2():
pass
def f3(x=3): # 默认参数赋值等号两侧不需加空格
pass
# ...
4、默认参数赋值等号两侧不需加空格
三、匿名函数
1. 基本形式
lambda 变量: 函数体
2. 常用用法
在参数列表中最适合使用匿名函数,尤其是与key = 搭配
- 排序sort() sorted()
输入:
ls = [(93, 88), (79, 100), (86, 71), (85, 85), (76, 94)]
ls.sort()
ls
输出:
[(76, 94), (79, 100), (85, 85), (86, 71), (93, 88)]
输入:
ls.sort(key = lambda x: x[1])
ls
输出:
[(86, 71), (85, 85), (93, 88), (76, 94), (79, 100)]
输入:
ls = [(93, 88), (79, 100), (86, 71), (85, 85), (76, 94)]
temp = sorted(ls, key = lambda x: x[0]+x[1], reverse=True)
temp
输出:
[(93, 88), (79, 100), (85, 85), (76, 94), (86, 71)]
- max() min()
输入:
ls = [(93, 88), (79, 100), (86, 71), (85, 85), (76, 94)]
n = max(ls, key = lambda x: x[1])
n
输出:
(79, 100)
输入:
n = min(ls, key = lambda x: x[1])
n
输出:
(86, 71)
四、面向过程和面向对象
面向过程——以过程为中心的编程思想,以“什么正在发生”为主要目标进行编程。 冰冷的,程序化的
面向对象——将现实世界的事物抽象成对象,更关注“谁在受影响”,更加贴近现实。 有血有肉,拟人(物)化的
- 以公共汽车为例
“面向过程”:汽车启动是一个事件,汽车到站是另一个事件。。。。
在编程序的时候我们关心的是某一个事件,而不是汽车本身。
我们分别对启动和到站编写程序。
"面向对象":构造“汽车”这个对象。
对象包含动力、服役时间、生产厂家等等一系列的“属性”;
也包含加油、启动、加速、刹车、拐弯、鸣喇叭、到站、维修等一系列的“方法”。
通过对象的行为表达相应的事件
五、重难点回顾