Python之函数
函数的简介
函数也是一个对象
函数可以用来保存可执行的代码,并且可以在需要是,对这些语句进行多次调用
创建函数
语法:
> def 函数名(形参1,形参2......):
> 代码块
注意:函数名必须符合标识符的命名规范(可以包含字母、数字、下划线但是不可以用数字开头)
# 函数中保存的代码不会立即执行
def fn():
print('这是第一个函数')
print('nihao')
print('黑猫警长')
print('hahaha')
print(fn)
fn()
# fn是指函数的对象,fn()调用函数
# print是函数对象print()调用函数
函数中保存的代码不会立即执行需要调用函数才会执行
<function fn at 0x00000231F89F33A8>
这是第一个函数
nihao
黑猫警长
hahaha
函数的参数
形参和实参
- 形参:
定义形参就相当于在函数的内部声明了变量,但是并不赋值 - 实参:
指定了形参,在调用函数的时候必须传递实参,实参将会赋值给对应的形参,简单来说有几个形参就要有几个实参。
# 可以用来求任意两个数的和
# 在定义一个函数,可以在括号的后面定义和数量不等的形参,多个形参用逗号隔开
def s(a, b):
print(a + b)
s(100, 90)
190
参数的传递方式
定义形参是,可以为形参指定默认值,指定默认值以后,如果用户传递了参数则默认值不会生效。如果用户没有传递,则默认值会生效
- 位置传参:
位置参数就是将对应位置的实参赋值给对应位置的形参
def fn1(a, b, c=10):
print('a =', a)
print('b =', b)
print('c =', c)
fn1(1, 2, 3)
def fn2(a, b, c=10):
print('a =', a)
print('b =', b)
print('c =', c)
fn2(1, 2)
def fn3(a=1, b=2, c=10):
print('a =', a)
print('b =', b)
print('c =', c)
fn3()
a = 1
b = 2
c = 3
a = 1
b = 2
c = 10
a = 1
b = 2
c = 10
- 关键字传参:
关键字参数可以不按照形参定义的顺序去传递,而是根据参数名进行传递。
# 关键字传参
def fn2(a,b,c):
print(a)
print(b)
print(c)
fn2(b = 1,c = 2,a = 3)
fn2(1,2,c = 20)
# 关键字传参和位置传参可以混合使用,但是关键字参数不可以写在位置参数的前面
#fn2(c = 20,1,2)
#import requests
#headers = {}
#url = ''
#requests.request(url,headers = headers)
3
1
2
1
2
20
实参类型
实参可以传递任意类型的对象(包括函数)
def fn2(a):
print('a = ',a)
#int型
b = 123
fn2(b)
#list型
b = [1,2,3,4]
fn2(b)
#bool型
b = True
fn2(b)
#str型
b = 'helllo world'
fn2(b)
a = 123
a = [1, 2, 3, 4]
a = True
a = helllo world
def fn2(a):
print('a = ',a)
def fn():
print(1)
fn2(fn)
a = <function fn at 0x00000266D2F00798>
函数在调用的时候解析器不会检查函数的实参类型的
例如:
def fn3(a,b):
print(a + b)
fn3(1,2)
# fn3(1,'2')unsupported operand type(s) for +: 'int' and 'str'整型和字符串型不可以进行相加运算
3
再看以下现象:
def fn4(a):
print('a = ',a)
c = 10
fn4(c)
print('c = ',c)
a = 10
c = 10
def fn4(a):
a = 20
print('a = ',a)
c = 10
fn4(c)
print('c = ',c)
a = 20
c = 10
经上述代码比对由此可知:a = 20相当于给a进行重新赋值的操作,给变量重新赋值是不会影响其他变量的
但是有例外:
如果形参执行的是一个可变对象,当我们修改对象(改对象的值)会影响到所指向的对象的变量
例如:
def fn4(a):
a[0] = 10
print('a = ',a)
c = [1,2,3]
fn4(c)
print('c = ',c)
a = [10, 2, 3]
c = [10, 2, 3]
那么如何在改变a的值的时候不会影响到c呢?
def fn4(a):
a[0] = 10
print('a = ',a,id(a))
c = [1,2,3]
# 第一种方式:浅复制
fn4(c.copy())
print('c = ',c,id(c))
# 第二种方式:切片
fn4(c[:])
print('c = ',c,id(c))
a = [10, 2, 3] 2160330681288
c = [1, 2, 3] 2160330612040
a = [10, 2, 3] 2160330681288
c = [1, 2, 3] 2160330612040
不定长参数
# 定义一个函数,可以求任意数的和
def s(a,b):
print(a + b)
s(1,2)
# s(1)实参不够 s() missing 1 required positional argument: 'b'
# s(1,2,3)实参过多 s() takes 2 positional arguments but 3 were given
3
如何避免出现这类的问题呢?
在定义函数的时候,可以在形参前面添加一个*,这样这个参数可以获取到所有的实参,它会将所有的实参保存到一个元组中
def fn(*b):
print('b = ',b,type(b))
fn(1,2,3,4,5,5,6,6,7,7,8)
b = (1, 2, 3, 4, 5, 5, 6, 6, 7, 7, 8) <class 'tuple'>
*b会接收所有的位置实参,并且将这些实参统一保存到元组中,这个过程叫:装包
这时求任意和的代码可以表示为
# 定义一个函数,可以求任意数的和
def s(*b):
# 定义一个变量保存结果
r = 0
# 遍历元组并将元组中的数进行累加
for i in b:
r += i
print(r)
s(1,2,300,78,8.90)
389.9
接下来看一下现象:
def fn2(a,b,*c):
print('a = ',a)
print('b = ',b)
print('c = ',c)
fn2(1,2,3,4,54)
a = 1
b = 2
c = (3, 4, 54)
所以实参先会依次给形参,剩下的实参会以元组的形式给带*的形参
可以有两个*
吗?
#def fn2(a,*b,*c):
# print('a = ',a)
# print('b = ',b)
# print('c = ',c)
#fn2(1,2,3,4,54)
SyntaxError: invalid synta
带*
的只有一个
将*
放入中间或者前面的形参是否可以?
def fn2(a,*b,c):
print('a = ',a)
print('b = ',b)
print('c = ',c)
#fn2(1,2,3,4,5) fn2() missing 1 required keyword-only argument: 'c'
fn2(1,2,3,4,c = 5)
def fn2(*a,b,c):
print('a = ',a)
print('b = ',c)
print('c = ',c)
fn2(1,2,3,b = 4,c = 5)
a = 1
b = (2, 3, 4)
c = 5
a = (1, 2, 3)
b = 5
c = 5
观察上面的代码可以知道,将*
单独放入中间或者前面的形参是不可以的,但是可以通过指定实参的值来实现
不定长参数不是必须写在最后,但是注意带*参数后面的所有的形参必须以关键字的形式定义
#def fn3(*b):
# print('b = ',b)
#fn3(b = 1,d = 2,c = 3)
fn3() got an unexpected keyword argument 'b'
def fn3(**b):
print('b = ',b)
fn3(b = 1,d = 2,c = 3)
b = {'b': 1, 'd': 2, 'c': 3}
带*
形参只接收位置参数,不能接收关键字参数
带**
形参可以接收其他的关键字参数,会将这些参数统一保存到一个字典中,字典中的key是实参的名字,字典的value是实参的值,而且只能有一个
def fn3(b,c,**a):
print('a = ',a)
print('b = ',b)
print('c = ',c)
fn3(b = 1,d = 2,c = 3,e = 50 ,f = 80)
a = {'d': 2, 'e': 50, 'f': 80}
b = 1
c = 3
*b处理的是位置参数**a处理的是关键字参数
import requests
headers = {}
url = ''
data = {}
requests.get(url,headers = headers,data = data)
参数解包
def fn3(a,b,c):
print('a = ',a)
print('b = ',b)
print('c = ',c)
fn3(1,2,3)
t = (1,2,3)
#fn3(t)fn3() missing 2 required positional arguments: 'b' and 'c'
fn3(t[0],t[1],t[2])
fn3(*t)
d = {'a':1,'b':2,'c':3}
fn3(**d)
a = 1
b = 2
c = 3
a = 1
b = 2
c = 3
a = 1
b = 2
c = 3
a = 1
b = 2
c = 3
函数的返回值
返回值就是函数执行以后的返回的结果
通过return来指定函数的返回值
def fn():
return 123
r = fn()
print(r)
print(fn())
此时要用一个变量来承接函数的返回值,之后再做打印操作
123
123
def fn():
#return 123
def fn2():
print('hahhah')
return fn2
r = fn()# 返回的是函数的对象<function fn.<locals>.fn2 at 0x00000255AC5C1798>
print(r)
r()# 返回的是函数的调用
return后面跟什么值,函数就会返回什么值,return后面可以跟任意的对象甚至可以是一个函数
<function fn.<locals>.fn2 at 0x00000133643219D8>
hahhah
如果没有或者只有一个return代码且后面没有返回值则自动返回一个None
def fn2():
return
r = fn2()
print(r)
def fn3():
print('hahha')
# 在函数中return后的代码都不会执行,return一旦执行函数自动结束
return
print('13')
r = fn3()
print(r)
None
hahha
None
将break和continue和return进行比对:
break:用来退出循环体
def fn4():
for i in range(5):
if i == 3:
break# 立即退出当前的循环
print(i)
print('循环执行完毕')
fn4()
0
1
2
循环执行完毕
continue:用来退出当次循环,继续执行下面的循环
def fn4():
for i in range(5):
if i == 3:
continue
print(i)
print('循环执行完毕')
fn4()
0
1
2
4
循环执行完毕
return:用来结束函数
def fn4():
for i in range(5):
if i == 3:
return
print(i)
print('循环执行完毕')
fn4()
0
1
2
求任意数的和
# 求任意数的和
def s(*b):
# 定义一个变量来保存结果
r = 0
# 遍历元组,并且将元组中的数进行累加操作
for i in b:
r +=i
return r
r = s(1,2)
print(r - 6)
-3
def fn():
return 123
print(fn)# 打印函数的对象
print(fn())# 调用函数,有返回值123
# fn是函数对象,打印fn就是在打印函数对象
# fn()是在调用函数,打印fn()实际上就是在打印fn()的返回值,有返回值就打印返回值,如果没有就返回None
<function fn at 0x000001E9E909C798>
123
文档字符串
通过help()可以查询python中内置函数的语法
语法 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 fn(a:int,b:str,c:bool)->int:
"""
这是一个文档字符串的案例
参数的作用
param a:作用 类型 默认值
param b:作用 类型 默认值
param c:作用 类型 默认值
return
"""
return 123
help(fn)
通过help()查询自己创建的函数的用法
Help on function fn in module __main__:
fn(a: int, b: str, c: bool) -> int
这是一个文档字符串的案例
参数的作用
param a:作用 类型 默认值
param b:作用 类型 默认值
param c:作用 类型 默认值
return
函数的作用域(scope)
作用域指的是变量生效的区域
a定义在函数的内部,所以它的作用域就是在函数的内部
b = 20
def fn():
a = 10
print('函数内部:',a)
print('函数的内部',b)
fn()
#print('函数外部:',a)
print('函数外部:',b)
函数内部: 10
函数的内部 20
函数外部: 20
函数在python中一共有两种作用域:
全局作用域
在函数以外定义的变量为全局变量,它的作用域为全局作用域,可以在程序的任意位置访问到
函数作用域
在函数内部定义的变量为局部变量,它的作用域为函数作用域,在函数的内部产生,在函数的内部销毁,调用一次函数就会产生一个函数作用域
def fn2():
a = 30
def fn3():
a = 80
print('fn3中:',a)
fn3()
fn2()
fn3中: 80
如何在函数的内部修改全局变量?
a = 20
def fn3():
# 如果希望在函数的内部修改全局变量,则需要使用一个global关键字来声明变量
global a# 生命在函数内部的使用a是全局变量,此时我们修改a,就是在修改全局变量
a = 10
print('函数内部:',a)
fn3()
print('函数外部',a)
函数内部: 10
函数外部 10
函数的命名空间
实际上就是一个字典,是一个专门用来存储变量的字典
locals()用来获取当前作用域的命名空间,返回的是一个字典
- 全局作用域
a = 20
b = 30
scope = locals()# 获取当前命名空间
print(scope)
print(a)
print(scope['a'])
# print(c)name 'c' is not defined
向scope中添加一个key-value(就相当于在全局中创建一个变量)
scope['c'] = 123
print(c)
- 函数作用域
a = 10
def fn4():
a = 20
scope = locals()# 要获取函数内部的命名空间
scope['b'] = 200
print(scope)
fn4()
{'a': 20, 'b': 200}
递归函数
尝试求10!
1 = 1
2 = 1 * 2
3 = 1 * 2 * 3
…
result = 1
for i in range(1,11):
result *= i
print(result)
3628800
定义一个函数来实现任意数的阶乘?
number = int(input("请输入您要计算的阶乘:例如(10!)"))
def fn(number):
result = 1
for i in range(1,number + 1):
result *= i
print(result)
fn(number)
请输入您要计算的阶乘:例如(10!)10
3628800
递归简单理解就是自己引用自己
递归函数就是在函数中调用自己
def fn1():
fn1()
fn1()
# 无穷递归,类似于死循环
number = int(input("请输入您要计算的阶乘:例如(10!)"))
def fn2(n):
# 参数n要求阶乘的数字
# 例如
# 10! = 9! * 10
# 9! = 8! * 9
# 8! = 7! * 8
# .....
# 1! = 1
# 基线条件:问题可以被分解为最小的问题,当满足基线条件时,递归就不在执行了,判断n为1的时候就不进行递归操作
if n== 1:
# 1的阶乘就是它本身1
return 1
# 递归条件:将问题继续分解的条件
return n * fn2(n - 1)
print(fn2(number))
请输入您要计算的阶乘:例如(10!)10
3628800
# 定义一个函数,来为任意数字做任意幂运算
num1 = int(input("请输入您要计算的幂运算的数字:"))
num2= int(input("请输入您要你计算的幂运算的次数:"))
def fn(n,i):
# 参数n做幂运算的数字 5 ** 2
# 参数i做幂运算的次数
# 10 ** 5 = 10 * (10 ** 4)
# 10 ** 4 = 10 * (10 ** 3)
# 10 ** 3 = 10 * (10 ** 2)
# ..........
# 10 ** 1 = 10
# 基线条件
if i == 1:
return n
# 递归条件
return n * fn(n,i - 1)
print(f'{num1}的{num2}次幂的结果是',fn(num1,num2))
请输入您要计算的幂运算的数字:3
请输入您要你计算的幂运算的次数:3
3的3次幂的结果是 27
# 创建一个函数,用来检查任意字符串是否是回文字符串
# 回文字符串:字符串从后向前念和从前向后念是一样的(aaabaaa)
i = input('请输入您的字符串:')
def fn(s):
# 参数s要检查的字符串
# 例如
# aabaa
s = list(s)
if len(s) < 2:
# 基线条件
# 先检查第一个字符和最后一个字符是否一致,如果不一致一定不是回文字符串
# 字符串的长度小于2,则字符串一定是回文字符串
return True
elif s[0] != s[-1]:
# 第一个字符和最后一个字符不相等,不是回文字符串
return False
return fn(s[1:-1])
# 递归条件
# 如果第一个字符和最后一个字符是一致的,那么就看其余的部分是否是回文字符串
result = fn(i)
if result == True:
print('您输入的字符串'+i+'是回文字符串')
else:
print('您输入的字符串'+i+'不是回文字符串')
请输入您的字符串:abcd
您输入的字符串abcd不是回文字符串
i = input('请输入您的字符串:')
def fn(s):
## 参数s要检查的字符串
## 例如
## aabaa
s = list(s)
#if len(s) < 2:
# # 基线条件
# # 先检查第一个字符和最后一个字符是否一致,如果不一致一定不是回文字符串
# # 字符串的长度小于2,则字符串一定是回文字符串
# return True
#elif s[0] != s[-1]:
# # 第一个字符和最后一个字符不相等,不是回文字符串
# return False
#return fn(s[1:-1])
if len(s) < 2:
return True
return s[0] == s[-1] and fn(s[1:-1])
# 递归条件
# 如果第一个字符和最后一个字符是一致的,那么就看其余的部分是否是回文字符串
result = fn(i)
if result == True:
print('您输入的字符串'+i+'是回文字符串')
else:
print('您输入的字符串'+i+'不是回文字符串')
请输入您的字符串:a
您输入的字符串a是回文字符串
高阶函数
高阶函数:接收函数作为参数,或者将函数作为返回值返回都是高阶函数
当我们使用一个函数作为参数时,实际上就是将一段代码传递到了目标函数
# 定义一个函数 将列表中所有的偶数保存到一个新的列表并且返回
lst = [1,2,3,4,5,6,7,8,9,10]
# 定义一个函数用来检查偶数
def fn2(i):
if i % 2 == 0:
return True
# 定义一个函数用来检查数字是否大于5
def fn3(i):
if i > 5:
return True
return False
# 3的倍数
def fn4(i):
if i % 3 == 0:
return True
return False
def fn(func,lst):
# 创建一个新的列表
new_list = []
# 对列表进行遍历
for n in lst:
# 判断奇偶数
if func(n):
new_list.append(n)
# 返回列表
return new_list
print(fn(fn2,lst))
[2, 4, 6, 8, 10]
匿名函数
filter(参数1,参数2)
可以从序列中过滤符合条件的元素,保存到一个新的序列中
参数1:根据该函数来过滤序列
参数2: 需要的过滤的序列
返回值:过滤后的新的序列
# 3的倍数
lst = [1,2,3,4,5,6,7,8,9,10]
def fn4(i):
if i % 3 == 0:
return True
return False
print(list(filter(fn4,lst)))
[3, 6, 9]
匿名函数 lambda函数表达式:专门用来创建一些简单的函数,他是函数另一种创建方式
语法:lambda 参数列表:返回值
def fn5(a,b):
return a + b
print((lambda a,b:a + b)(1,2))
3
fn6 = lambda a,b :a + b
print(fn6(100,500))
600
闭包
将函数作为返回值返回,这种形式称之为闭包
def fn():
a = 123
# 函数内部定义一个函数
def fn1():
print("我是fn1...",a)
# 将内部函数fn1作为返回值返回
return fn1
#print(fn)
# r这个函数总是可以访问到fn()内部的变量
r = fn()
r()
# print(a)name 'a' is not defined
# 通过闭包可以创建一些在只有当前函数才可以访问的变量,可以将一些私有的数据藏到闭包中
我是fn1... 123
nums = [1,2,3,4,5,6]
# 求列表中元素的平均值
print(sum(nums)/len(nums))
3.5
# 定义一个函数求平均值
#nums = []
def make_average():
nums = []
def average(n):
# 将n添加到列表中
nums.append(n)
return sum(nums)/len(nums)
# 将函数作为返回值返回
return average
#print(average(10))
##nums.append('hello')
#print(average(30))
average = make_average()
print(average(15))
# 形成闭包的条件:1.函数嵌套2.将内部函数作为返回值返回3.内部函数必须要使用到外部函数的变量
15.0
装饰器的引入
def add(a,b):
print('函数开始执行')
r = a + b
print('函数执行结束')
return r
def mul(a,b):
return a * b
r = add(1,2)
print(r)
函数开始执行
函数执行结束
3
我们可以修改函数中的代码来完成这个需求,但是会产生一些问题:
1.如果要修改的函数过多,修改起来会比较麻烦
2.不方便后期的维护
3.违反开闭原则(OCP)程序的设计,要求开发对程序的扩展,要求关闭对程序的修改
# 希望在不修改原函数的情况下,对函数进行扩展
#def fn():
# print('我是fn函数')
# 可以定义一个新的函数来对原函数进行扩展
def add(a,b):
r = a + b
return r
def new_add(a,b):
print('函数开始执行')
r = add(a,b)
print('函数执行结束')
return r
r = new_add(1,2)
print(r)
函数开始执行
函数执行结束
3
装饰器的使用
#装饰器的使用
def add(a,b):
r = a+ b
return r
def fn():
print('我是fn函数')
def start_end(old):
# 用来对其他的函数进行扩展,是其他的函数可以在执行前打印开始执行,执行结束后打印执行结束
# 创建一个新的函数
def new_function(*args,**kwargs):
# 第一个参数接收所有位置参数 第二个参数接收所有关键字参数
print('开始执行')
# 调用被扩展的函数
result = old(*args,**kwargs)
print('执行结束')
return result
return new_function
f = start_end(add)# start_end() missing 1 required positional argument: 'old'
r = f(1,2)
print(r)
f = start_end(fn)
r = f()
#print(r)
f2 = start_end(add)
r = f2(123,123)
print(r)
# 例如:像start_end()这一类的函数我们就称之为装饰器,通过装饰器可以在不修改原来函数的情况下对原来的函数进行扩展,在开发中,我们都是通过装饰器来扩展函数的功能
# 通过装饰器对speak()函数进行扩展
@start_end
def speak():
print('你好')
speak()
开始执行
执行结束
3
开始执行
我是fn函数
执行结束
开始执行
执行结束
246
开始执行
你好
执行结束
实例:汉诺塔游戏
现在有ABC三根柱子。要求:将A柱子所有的圆盘放入到C柱子,在移动的过程中可以借助B柱子,并且规定大圆盘不可以放在小圆盘的上面,每次只可以移动一个盘子,用递归的方式来解决汉诺塔问题
# 现在有ABC三根柱子。要求:将A柱子所有的圆盘放入到C柱子,在移动的过程中可以借助B柱子,并且规定大圆盘不可以放在小圆盘的上面,每次只可以移动一个盘子,用递归的方式来解决汉诺塔问题
# 定义一个函数来解决汉诺塔问题
# 1.如果只有一个盘子A->C
# 2.如果有大于等于2个盘子的情况下,我们总可以把他们看成是2个盘子,一个是最下面的最大盘子,另一个是最下面盘子上面的多个盘子(或者一个盘子)
# 2.1先把最上面的一个或者多个盘子放到B柱子A->B
# 2.2把最下面的盘子从A->C
# 2.3把B柱子上面的一个或者多个盘子放入到C柱子B->C
def hannuoTower(num,a,b,c):
# 参数num代表的是盘子
# a,b,c分别代表A B C柱子
# 递归函数的写法
# 1.基线条件
if num == 1:
print('第 1 个盘子从',a,'->',c)
# 2.递归条件
else:
# n>=2的情况下
# 2.1先把最上面的一个或者多个盘子放到B柱子A->B,有可能借助到C,num-1代表除了最下面的盘女子
hannuoTower(num-1,a,c,b)
# 2.2把最下面的盘子从A->C
print('第',num,'个盘子从',a,'->',c)
# 2.3把B柱子上面的一个或者多个盘子放入到C柱子B->C,可能借助到A
hannuoTower(num-1,b,a,c)
hannuoTower(3,'A','B','C')
第 1 个盘子从 A -> C
第 2 个盘子从 A -> B
第 1 个盘子从 C -> B
第 3 个盘子从 A -> C
第 1 个盘子从 B -> A
第 2 个盘子从 B -> C
第 1 个盘子从 A -> C
标签:10,return,函数,Python,fn,print,def
From: https://blog.51cto.com/u_15016660/6296634