函数的主要作用: 打包代码
最大程度实现代码的重用减少冗余代码
将不同代码进行封装、分解从而降低结构复杂度,提高代码可读性
# 创建
def my_func():
pass
# 调用
my_func()
def my_func(name, times):
for i in range(times):
print(f"我喜欢{name}")
my_func(name="python", times=5)
形式参数(形参)
实际参数(实参)
我们在传参的时候不仅可以这样
number = div(x=6, y=0)
还可以这样
number = div(6, 0)
那么可以混着用么?
# 这样是可以的
number = div(6, y=0)
# 但这样不行
number = div(x=6, 0)
关键字参数必须在,位置参数后面
我们可以定义一个求圆面积的函数是这样的
def func(r,pai):
print(2*pai*r)
我们都知道 pai
是3.1415926…
那么我们可以不可以让这个参数默认为 3.14
呢,如果需要要精细那么再让用户自己传入.
def func(r, pai=3.14):
print(2 * pai * r)
# 可以传
# func(3, 3.1415926)
# 也可以不传
func(3)
有时候我们不知道用户要输入多少参数
例如:print
这时我们就需要使用到我们的收集参数
def func(*args):
print(args, type(args)) # (1, 2, 3, 4)
func(1, 2, 3, 4)
我们发现,传入这么多值其实用的元组的打包
性质
字典形式的收集参数
def func(**kwargs):
print(kwargs) # {'a': 1, 'b': 2, 'c': 3}
func(a=1, b=2, c=3)
收集参数就是 打包操作
那么我们 传入的时候还有一个变量解包操作
def func(a, b, c, d):
print(a, b, c, d) # 1 2 3 4
f = (1, 2, 3, 4)
g = {'a': 1, "c": 3, "b": 2, "d": 4}
func(*f)
func(**g)
例如:
我们需要使用 return
让我们的自定义函数实现返回
实现一个除法的函数
- 实现基本的除法
- 除数不能为0
def div(x, y):
if y == 0:
return "除数不能为0"
else:
return x / y
number = div(x=6, y=1)
print(number)
return后代码将不会在继续向下执行,这样代码就可以简化成
def div(x, y):
if y == 0:
return "除数不能为0"
return x / y
number = div(x=6, y=1)
print(number)
在想想我们学过的三目运算,代码就可以简化成这样
def div(x, y):
return x / y if y != 0 else "除数不能为0"
number = div(x=6, y=0)
print(number)
如果我们不写return 那么函数也会返回一个
None
变量或函数起作用的范围
全局变量
变量定义的位置不在任何函数中
a = 0 # 全局变量
def func():
print(a)
func()
我们想修改全局变量时,可能不如我们所意
a = 0 # 全局变量
def func1():
a = 1 # 局部变量
print(a) # 输出的是局部变量 1
func1()
print(a) # 输出的是全局变量 0
我们可以使用 global
从而实现我们修改的目的
a = 0 # 全局变量
def func1():
global a
a = 1
print(a)
func1()
print(a)
局部变量
def func1():
b = 1 # 局部变量
def func2():
print(b) # NameError: name 'b' is not defined
func1()
func2()
- 函数内的函数,无法在外部被调用
- 函数内定义的变量,可以使用
nonlocal
关键字进行修改
def func1(x):
print(x)
def func2():
nonlocal x
x = 5
print(x)
func2()
print(x)
func1(1)
我们都知道嵌套函数如果想要调用内层函数,可以在外层函数的内部调用,然后我们调用外部函数,从而实现内部函数的使用。
def func():
x = 10
def func2():
print(x)
func2()
func()
那么我们有没有什么方法,可以在调用外部函数的时候不执行内层函数,而在外部代码中选择执行位置呢?
我们可以将内部函数的引用返回
def func():
x = 10
def func2():
print(x)
return func2
f2 = func()
print("func调用结束")
f2()
注意 : 返回的是函数名,无需加小括号
那么通过这个特征我们可以实现什么呢
可以实现工厂函数
我们调用外部函数传入不同参返回内部函数时,内部函数将会一直受到外部参数的影响
def func(x):
def func2(y):
return y ** x
return func2
square = func(2)
cube = func(3)
print(square(5)) # 25
print(cube(5)) # 125
我们还能怎么用闭包
控制某个单位移动
def func(x, y):
def func2(x1, y1):
nonlocal x, y
x += x1
y += y1
print(f"x = {x} , y = {y}")
return func2
f1 = func(0, 0)
f1(1, 2)
f1(-2, 1)
函数可以传参,函数本身也可以被当作参数被传入
def func1():
print("hello world func1")
def func2(func):
print("func2 start")
func()
print("func2 end")
func2(func1)
这样我们就可以实现一个计算函数耗时的函数
import time
def func1():
time.sleep(1)
print("hello world func1")
def func2(func):
s = time.time()
func()
e = time.time()
print(f"程序耗时:{e - s}")
func2(func1)
这样写是可以实现,但每次使用都得去专门调用这个函数。我们想要更加简洁可以使用装饰器
import time
def func1(func):
def func2():
s = time.time()
print("开始")
func()
print("结束")
e = time.time()
print(f"程序耗时:{e - s}")
return func2
def func3():
time.sleep(1)
print("hello world func3")
func = func1(func3)
func()
感觉还不如不用来的简便是么?我们可以使用语法糖 @func1
就可以直接调用装饰器了
import time
def func1(func):
def func2():
s = time.time()
print("开始")
func()
print("结束")
e = time.time()
print(f"程序耗时:{e - s}")
return func2
@func1
def func3():
time.sleep(1)
print("hello world func3")
func3()
def add(func):
def inner():
print("add")
x = func()
print(x, "add")
return x + 1
return inner
def cube(func):
def inner():
print("cube")
x = func()
print(x, "cube")
return x ** 3
return inner
def square(func):
def inner():
print("square")
x = func()
print(x, "square")
return x + 2
return inner
@add
@cube
@square
def test():
print(1)
return 3
print(test())
执行结果如下
add
cube
square
1
3 square
5 cube
125 add
126
原本模样
import time
def func4(msg):
def func1(func):
def func2():
print(f"msg = {msg}")
s = time.time()
print("开始")
func()
print("结束")
e = time.time()
print(f"程序耗时:{e - s}")
return func2
return func1
def func3():
time.sleep(1)
print("hello world func3")
func3 = func4(msg="A")(func3)
func3()
使用语法糖
import time
def func4(msg):
def func1(func):
def func2():
print(f"msg = {msg}")
s = time.time()
print("开始")
func()
print("结束")
e = time.time()
print(f"程序耗时:{e - s}")
return func2
return func1
@func4(msg="B")
def func3():
time.sleep(1)
print("hello world func3")
func3()
我们正常写一个函数是这样的
# 求平方
def square(x):
return x * x
print(square(2))
而lambda是这样的
# 求平方
square = lambda x: x*x
print(square(2))
我们看出来了lambda是比较简洁的,但好像并没有那么简洁那么我们来看看这个
求出列表中所有值的平方
正常写法
list_x = [1, 2, 3, 4, 5, 6, 7, 8, 9]
def square(x):
return x * x
list_y = []
for x in list_x:
list_y.append(square(x))
print(list_y)
使用map
list_x = [1, 2, 3, 4, 5, 6, 7, 8, 9]
def square(x):
return x * x
print(list(map(square,list_x)))
使用lambda和map
list_x = [1, 2, 3, 4, 5, 6, 7, 8, 9]
print(list(map(lambda x: x * x, list_x)))
小练习 斐波那契数列
斐波那契数列 由0和1开始,之后的斐波那契数就是由之前的两数相加而得出例如:
0,1,1,2,3,5,8…
解决方法
init_number1 = 0
init_number2 = 1
print(init_number1)
print(init_number2)
for i in range(0, 10):
init_number1 = init_number1 + init_number2
print(init_number1)
init_number1, init_number2 = init_number2, init_number1
当函数遇到 return
那么这个函数就算是结束了,函数中做过的工作,保存的局部变量都会丢失。 再次调用的时候一切都是从头再来。
有没有什么办法让函数在退出之后还能保留状态呢?
将函数中的 return
替换为 yield
即可
def counter():
i = 0
while i <= 5:
yield i
i += 1
c = counter()
print(c) #
生成器 和 迭代器、列表、元组等可迭代对象不一样。
生成器
我们可以看作一个制作机器,每次被调用将会返回一个数据,并且会记住当时的状态。
而列表、元组这些可迭代对象
则是容器,他们里面存放的是早已准备好的全部数据
生成器
可以看作是特殊的 迭代器
def counter():
i = 0
while i <= 5:
yield i
i += 1
c = counter()
print(next(c)) # 0
print(next(c)) # 1
print(next(c)) # 2
print(next(c)) # 3
print(next(c)) # 4
print(next(c)) # StopIteration
def fib():
back1, back2 = 0, 1
while True:
yield back1
back1, back2 = back2, back1 + back2
f = fib()
print(next(f)) # 0
print(next(f)) # 1
print(next(f)) # 1
print(next(f)) # 2
print(next(f)) # 3
print(next(f)) # 5
print(next(f)) # 8
t = (i**2 for i in range(10))
for i in t:
print(i)
生成器表达式和列表表达式最大的区别在于,列表推导式会将所有数据一下子生产出来并放到列表中。但生成器表达式,一次只会返回一个值。
什么是递归? 自己调自己
def funcA():
print("我被调用了")
funcA()
funcA()
你会发现它一直在调用自己,我们可以加些代码让它停下来。
def funcA(count):
if count > 0:
print("我被调用了")
count -= 1
funcA(count)
funcA(10)
由上面代码可知,递归必须需要一个 结束条件
并且每次调用都会向着这个结束条件去推进
小练习 一
求一个数的阶乘
迭代实现
def factIter(n):
result = n
for i in range(1, n):
result *= i
return result
print(factIter(10))
递归实现
def factRecur(n):
if n == 1:
return 1
else:
return n * factRecur(n - 1)
print(factRecur(10))
小练习 二
斐波那契数列
def factRecur(n):
if n == 1 or n == 2:
return 1
else:
return factRecur(n - 1) + factRecur(n - 2)
print(factRecur(12))
如果你将斐波那契的12换成120 你将会发现递归的问题 效率慢
因为他需要将所有函数全部调用,最终一层一层返回,所以效率一定没有迭代快。
我们使用help()方法可以展示出某个函数的函数文档
help(print)
会展示以下内容
Help on built-in function print in module builtins:
print(...)
print(value, ..., sep=' ', end='\n', file=sys.stdout, flush=False)
Prints the values to a stream, or to sys.stdout by default.
Optional keyword arguments:
file: a file-like object (stream); defaults to the current sys.stdout.
sep: string inserted between values, default a space.
end: string appended after the last value, default a newline.
flush: whether to forcibly flush the stream.
那么我们如何去给自己的函数编写文档呢?
def add(n1, n2):
"""
功能:
用来将n1和n2相加
:param:
- n1 一个数字
- n2 另一个数字
:return
n1和n2的和
"""
return n1 + n2
help(add)
"""
Help on function add in module __main__:
add(n1, n2)
功能:
用来将n1和n2相加
:param:
- n1 一个数字
- n2 另一个数字
:return
n1和n2的和
"""
我们可以通过以下方法告知其他代码开发者我们的函数需要什么类型的数据,以及返回什么类型的数据
def add(n1: int, n2: int) -> int:
return n1 + n2
help(add) # add(n1: int, n2: int) -> int