Py002-01-04函数

函数

  • 解决代码重复问题(给一坨代码起个名字法)
  • 方便程序扩展
  • 使程序变得易维护
1
2
3
4
5
6
7
8
9
10
11
12
13
def 发邮件(内容):
连接邮箱服务器
发送邮件
关闭连接

def sayHi(): # 定义函数
print('hello')

sayHi() # 执行代码

传参数------------------------
def sayHi(name): # 定义函数
print('hello',name)

形参实参的问题不写了。写的实在太多了

默认参数

就记住一个规则:默认参数必须在位置参数后面

1
2
3
4
5
6
7
8
9
10
11
12
def userInfo(name,age,gender,city='USA'):
print(name,age,gender,city)

如果默认参数提前了呢?---------------------------
ide会爆红,因为 那样就会影响后面的位置参数

def userInfo(name,age,city='USA',gender):

此时你就写三个参数
userInfo('a','2','man')
然后报错
non-default argument follows default argument

关键参数

铁律:位置参数不能放在关键字参数后面

1
2
3
4
5
6
7
8
9
10
def userInfo(name,age,gender='man',city='USA'):
print(name,age,gender,city)

调用可以这样
userInfo("山炮",gender="woman",age=22,city='china')

绝不可以这样
userInfo("山炮",gender="woman",22,city='china')
这样也不行
userInfo("山炮",22,age=22,city='china')

非固定参数

场景:以前小公司只有一个维护人员,功能出现问题就给维护人员报警提示

1
2
def send_msg(msg,user):
print(msg,user)

后来公司壮大了,10个运维人员,如何发给10个人呢

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 多个人你肯定不能这样
# send_msg('别浪了','a1')
# send_msg('别浪了','a2')
# send_msg('别浪了','a3')
# send_msg('别浪了','a4')
# send_msg('别浪了','a5')
# ....

#----------------------------
# 报警:10个运维人员咋办? 修改如下 在users前加上「*」
def send_msg(msg,*users):
for u in users:
print(msg,u)

send_msg('别浪了','a1','a2','a3','a4') # 将第一个参数后面的参数打包给 users
此时 users就变为一个元组

# 如果参数中出现 *users参数的就可以不再固定个数,传过来的所有数据打包为一个元祖

# *users 实际有种专业写法就是 ---------> *args

传参的两种方式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
def send_msg(msg,*users):
for u in users:
print(msg,u)
# 方式一
send_msg('别浪了','a1','a2','a3','a4')


# 如果向传一个列表呢?
send_msg('别狼了',['a1','a2','a3','a4']) # 这样实际上是 (['a1','a2','a3','a4'])
# 方式二

该咋办呢? 在传递的列表前加上一个 * 就可以了
send_msg('别狼了',*['a1','a2','a3','a4'])
这样就会这样
*['a1','a2','a3','a4'] ===> 'a1','a2','a3','a4'

如果有奇葩队友这样写参数咋办?

1
2
3
4
5
6
7
def send_msg(msg,*users,age):
pass

# send_msg('别狼了','alex',22) # 这样就会报错 因为 这种参数传递 都会把 alex 22 给 users 到 age的时候就没有参数了

# 有木有解决办法 使用关键字参数
send_msg('别狼了','alex','xxx',age=22)

非固定参数二

1
2
3
4
5
6
7
8
def func(name,*args,**kwargs):
print(name,args,kwargs)

func('alex',22,'qq','500w') # alex (22, 'qq', '500w') {}
# 最后一个字典代表关键字参数

func('alex',22,'qq','500w',phone='1323123123',addr='北京')
# alex (22, 'qq', '500w') {'phone': '1323123123', 'addr': '北京'}

你觉得写每个关键字参数太麻烦,于是改进了下

1
2
3
4
5
6
7
d = {"phone":"1323123123","add":"北京"}
func('alex',d) # 为啥参数跑 元组里去了,因为被当作非固定参数
# alex ({'phone': '1323123123', 'addr': '北京'},) {}

# 对于字典, 应该这样
func('alex',**d)
# alex () {'phone': '1323123123', 'addr': '北京'}

返回值

1
2
3
4
5
6
7
8
9
10
11
def fn():
return 1

print(fn()) # 1 返回1


def fn2():
pass

print(fn2()) # 返回 None
# 没写return 返回None
  • 能不能返回多个值? 不能 肯定是被包裹在一个容器里
1
2
3
4
5
6
7
def fn():
return 1,2
print(fn()) # [1,2]

def fn2():
return [1,2]
print(fn2()) # [1,2]

局部变量

  • 定义在函数外部的一级代码的变量叫全局变量
  • 在函数内定义的变量就是局部变量
  • 函数内部可以引用全局变量,如果函数内外都有一个name变量,函数查找是由内而外的
  • 局部可以引用全局变量,但不能修改 如name = ‘xx’实际是函数内部声明了一个局部变量name
1
2
3
4
5
6
7
8
9
10
# 全局
name = 'aaa'
def xxx():
# 局部
name = 'bbb'
print(name)


xxx() # 'bbb'
print(name) # 'aaa'

如果内部没有定义name 能直接打印吗? 可以

1
2
3
4
5
6
7
8
# 全局
name = 'aaa'
def xxx():
print(name) # 它会向父级作用域查找 有就打印,没有报错


xxx() # 'aaa'
print(name) # 'aaa'

我就是要修改全局变量

  • global声明一下你就可以改了
1
2
3
4
5
6
7
8
9
# 全局
name = 'aaa'
def xxx():
global name
name = 'bbb'
print(name)

xxx() # 'bbb'
print(name) # 'bbb'

让你诧异的问题

如果全局变量是个列表呢!而且也不进行global声明可以修改吗?

可以

1
2
3
4
5
6
7
8
9
10
11
12
13
names = ['a','b','c','d']

def xxx2():
del names[2]
names[1] = 'boss'
print(names)

xxx2() # ['a','boss','d']
print(names) # ['a','boss','d']

# 实际names整体是一个内存地址 它内部元素又是单独的地址可以修改

# 如果我就要整体换names里的东西呢? global声明呗

嵌套函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
age = 19
def wrapper():
age = 73
print(age)
def inner():
age = 84
print(age)
inner()

wrapper()
# 73
# 84

# 原理就是作用域从内到外,逐级向上查找

来看各种恶心的调用版本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
age = 19
def wrapper():
age = 73
def inner():
print(age)
inner()

wrapper() # 73
再看-----------------------------
age = 19
def wrapper():
def inner():
print(age)
age = 73 # 声明在 函数后
inner()

wrapper() # 73
再看-----------------------------
age = 19
def wrapper():
def inner():
print(age)
inner()
age = 73 # 声明在 函数调用后

wrapper() # 报错
# 调用inner()的时候会找age 它会向全局找,但是函数调用的下面又声明了一个age所以就不知道找谁了,于是报错

# 如何解决
1. 要么声明在inner()之前
2. 要么在调用前声明global age
闭包
1
2
3
4
5
6
7
8
9
def wrapper():
aa = 1
def inner():
print(aa)
return inner

# 内部函数访问外部函数的变量就叫闭包
numAdd = wrapper()
numAdd()

匿名函数

声明一个匿名函数

1
2
3
4
5
6
7
8
# 传统方式
def fn(x,y):
return x**y

# 匿名函数
func = lambda x,y:x**y

func(2,5) # 32

注意:

lambda返回值不支持复杂的条件判断,只支持三元运算,不像js的箭头函数可以用{}包含起来

1
func = lambda x,y:x*y if x<y else x/y

匿名函数的用途

1
2
3
4
5
6
7
def fn(n):
return n*n

data = list(range(10))
map(fn,data)
--------------------------------
map(lambda x:x*x,data)

匿名函数总结:

  • 节省代码量
  • 看着高大上

高阶函数

只要满足以下任意条件就是高阶函数

  • 把函数当作参数
  • return 返回另一个函数
1
2
3
4
5
6
7
8
def xx(a):
return a + 1

def yy(fn,n):
return fn(n) # 返回 给参数函数fn 传递参数n并返回执行结果

res = yy(xx,2) # 传递 xx 函数
print(res) # 3