Python-1.函数(入门篇)

函数

什么是函数:

在python中函数是组织代码的最小单元

可重用,功能单一

输入(参数)===> 输出(返回值)

python的函数,并不是数学中函数,python也可以使用math模块和cmath模块实现数学中的函数,也可以使用abs,cmp,exp,mod,max,min等方法实现数学中的函数,详见

定义函数

以下操作建议使用Jupyter来执行,部署jupyter步骤

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
def funcname():
print(1)
return 1

funcname()
1
out>1

#def表示定义一个函数,funcname是函数名,函数后面括号是参数列表(可不定义),最后使用:开始函数体
#print(1+2) 函数体是正确的python语句,可以包含任意结构。
#return语句表示函数返回值.返回结束


#函数有输入(参数)和输出(返回值),函数式一个代码单元,将输入转换成输出
#实例
def funcname(a):
return a

funcname('zhuxuyue')
out>'zhuxuyue'

#funcname使用函数名来调用
#函数 调用传入的参数 必须和 函数定义时参数 匹配,如果不匹配则抛出TypeError错误
#定义函数的时候不会执行函数体里的内容,调用时才执行其中语句块

函数参数

函数参数包含:位置参数,关键字参数,默认参数,可变参数,可变关键字参数,参数结构,命名关键字参数(keyword-only)

位置参数和关键字参数
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
def add(x,y,z):
return (x+y-z)

add(10,20,30)
out>0

add(20,30,10)
out>40

#在定义函数时,如果有多个参数,参数间使用空格
#调用时参数 按照 顺序 传入定义时参数的顺序
#在位置参数中 调用传入参数非常重要,直接影响结果
#如果调用时传入的参数 多于/少于 定义时的参数,都会抛出TypeError错误


def add(x,y,z):
return (x+y-z)

#关键字参数
add(z=20,x=10,y=30)
out>20
#调用参数时,可使用定义时的变量名称传入,这种传参也叫关键字参数,关键字参数和顺序没有关系,只要能对应定义时参数就可以了

add(10,y=30,z=20)
out>20
#位置参数可以和关键字参数一起使用。

add(10,y=30,20)
out>TypeError
#一起使用时,一旦关键参数在位置参数前,则会抛出TypeError异常
默认参数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
def add(x,y,z=100):
return (x-y+z)

add(10,20,110)
out>100

add(10,20,z=20)
out>10

add(x=20,z=30,y=20)
out>30

add(z=20,10,20)
SyntaxError

#定义参数是如果有默认值,在调用时如果不传递参数,会使用默认值,
#在调用时修改了默认参数,则调用参数值会覆盖默认参数值
#在调用时默认参数必须在位置参数之后,否则会抛出SyntaxError异常
位置可变参数和可变关键字参数
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
32
33
34
35
36
37
38
39
40
def sum(x,y)
return x+y
#在调用时只能输入一个个的数值才能一个个去计算,假设要对多个值求值怎么办?

#例子
def sum(lst):
print(type(lst))
ret = 0
for x in lst:
ret +=x
return ret
sum([1,2,3,4,5])
<class 'list'>
out>15
#这种可以实现,但是不优雅,通常在python中使用下面的方法实现,

#参数加*表示位置可变参数,
def sum(*lst):
print(type(lst))
ret = 0
for x in lst:
ret += x
return ret
sum(1,2,3,4,5)
<class 'tuple'>
out>15
#*lst参数加星号,表示该参数是可变参数,构成一个元祖,此时只能通过位置参数进行传参。

def connect(**kwargs):
for k,v in kwargs.items():
print(k,v)
connect(host='127.0.0.1',user='root',port=3306)
port 3306
user root
host 127.0.0.1
#**kwargs加两个星号,表示参数是可变的,可以接受任意多个参数,这些参数构成一个字典,需要通过关键字传参


#位置可变参数 参数前加一个*号,构成元祖,传参只能以位置参数传参
#关键字可变参数 参数前加两个**号,构成字典,传参只能以关键字参数传参
注意事项
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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
def fn(**kwargs,*args):
print(kwargs,args)
SyntaxError

def fn(*args,**kwargs):
print(kwargs,args)

#定义参数时 位置可变参数可以和关键字可变参数一起使用
#但是关键字可变参数必须在位置可变参数之后,否则会抛出SyntaxError


def fn(x,y,*args,**kwargs):
print(x,y,args,kwargs)

fn(1,2,3,4,5,y=2,a=2,b=3)
TypeError

fn(1,2,3,4,5,a=2,b=3)
1 2 (3,4,5) {'a':2,'b':3}

#普通参数可以和可变参数一起使用
#但是传参必须匹配,同一个参数不可以出现两次


def fn(*args,x):
print(args)
print(x)

fn(1,2,3,4,5):
TypeError

fn(1,2,3,4,x=5):
1,2,3,4
5

#位置可变参数可以在普通参数之前
#但是在位置可变参数之后的普通参数变成keyword-only参数
#keyword-only 只能以关键字方式传输叫keyword-only


def fn(**kwargs,x):
print(kwargs)
print(x)
syntaxError
#当默认参数和可变参数一起出现的时候,默认参数相当于普通参数
#关键字参数不允许在普通参数之前


-----------------------------------------------------------
#总结:
#默认参数靠后
#可变参数靠后
#默认参数可变参数不能同时出现

#如果既想有默认参数,又有可变关键字参数怎么办
#示例
def connect(host='127.0.0.1',port=3306,user='root',passwd='',db='test',**kwargs):
pass

def connect(**kwargs):
host = kwargs.pop('host','127.0.0.1)
#字典可以用pop方法,需要指定默认值,如果不指定默认值的话会,如果找不到会抛出异常
参数解构
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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
#示例:
def add(x,y):
ret = x+y
return ret
a = [1,2]
#这如何将a里面列表传入到add定义的参数里面?

add(a[0],a[1])
out>3
#这种方式不是很优雅,python是个优雅的语言:)

add(*a)
out>3
#在位置参数中,加星号,可把一个可迭代对象结构成位置参数

#拿刚才可变位置参数来说
def sum(*lst):
print(type(lst))
ret = 0
for x in lst:
ret +=x
return ret
sum(*range(1,101))
out>5050

#需要注意的是
#参数结构,是发送在调用的时候才结构
#可变位置参数,发生在函数定义的时候


#既然可变参数可以参数结构,那么我们试试可变关键字参数是不是也可以
#可变关键字参数结构需要加**
def add(x,y,z):
return x,y,z
dic = {'x':1,'y':100,'z':1000}
add(**dic)
(1, 100, 1000)

def add(**kwarg):
for k,v in kwarg.items():
print(k,v)
dic = {'x':1,'y':100,'z':1000}
add(**dic)
x 1
y 100
z 1000


#总结:
#参数解构两种形式:可变位置参数解构,可变关键字参数解构
#* 结构对象是可迭代对象,结构的结果是位置参数
#** 结构对象是字典,解构结果是关键字参数

#注:
#再次强调一定要区分 参数解构 和 可变参数。
#参数解构 是发生在函数调用
#可变位置参数 是发送在函数定义

def sum(*lst): #这里(*lst) 表示可变位置参数,
print(type(lst))
ret = 0
for x in lst:
ret +=x
return ret
sum(*range(1,101)) #这里(*range(1,101)) 表示参数解构
out>5050

#解构限制在哪?
#实例
def fn(**kwargs):
print(kwargs)
fn(**{'a':1})
{'a':1}

fn(**{1:1})
TypeError
#关键字参数结构,key必须是str类型,否则抛出TypeError

#参数结构也可可以是set类型
def funcname(*arg):
return arg
funcname(*{5,12,3,4,5,12,34,3,4,0})
out>(0, 34, 3, 4, 5, 12)
#通常用的不多
命名关键字参数
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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
#命名关键字参数,也叫(Keyword-only) 是在python3新增的一个特性

def fn(*,x):
return x

fn(x=3)
out>3

fn(1,x=2)
TypeError

#在加星号之后的参数只能通过关键字参数传递,叫keyword-only参数:)
#可变位置参数之后的参数也是keyword-only参数,必须以关键字方式传入


#实例:
def fn(x,*,y):
return x,y
fn(1,y=2)
out>(1,2)

fn(x=1,y=2)
out>1,2

fn(y=3,x=1)
out>1,3

fn(x=1,2)
SyntaxError

#在函数定义时候,可以跟普通参数使用,但是在keyword-only后必须用关键字方式传参
#通常我们使用keyword-only参数必须有默认值
#类似:
def fn(x,*,y=10):
return x,y

fn(12)
out>(12, 10)

fn(y=11,x=20)
out(20,11)

fn(x=20,20)
SyntaxError




#练习
#写个函数连接mysql数据库,必须输入用户名、密码,但是主机,字符集,端口可以使用默认的localhost,utf-8,3306
def mysqlconnect(host='localhost',characterset='utf-8',port='3306',*,user,passwd):
print('''
user:{}
password:{}
host:{}
port:{}
characterset:{}
'''.format(user,passwd,host,port,characterset))

mysqlconnect(user='root',passwd='zhuxyzuishuai')
user:root
password:zhuxyzuishuai
host:localhost
port:3306
characterset:utf-8

函数返回值

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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
#----------------------python返回值return------------------------
#实例1:
def fn(x,y):
print(x,y)
return x,y
fn(1,2)
1,2
out>1,2

def fn(x,y):
return x,y
print(x,y)
fn(1,2)
out>1,2
#可以看出 return 可以返回返回值,return之后语句不会执行

#实例2:
def fn(x,y):
if x > y:
return x
return y
fn(3,2)
out>3

fn(2,3)
out>3
#一个函数体可以定义多个return语句,执行到哪个return语句就返回那个return结果,并结束函数

#实例3
def fn(x):
for i in range(x):
if i > 3:
return i
return '小于3'
fn(10)
out>'小于3'

def fn(x):
for i in range(x):
if i > 3:
return i
return '小于3'
fn(10)
out>4
#return 也可提前结束循环体


#实例4
def fn(x):
x = 100
fn(1)
func = fn(1)
#如果一个函数没有返回值,隐藏式返回None
type(func)
out>NoneType
#可使用type查看函数

def fn(x):
pass
#==等效==>
def fn1(x):
return
#==等效==>
def fn2(x):
return None

#--------------------python返回值return作用-----------------------
def fn():
return 3,5 #封包
ret = fn()
type(ret)
out>tuple
#当函数需要返回多个值的时候,可以将返回值封装成元祖,封装在return中


x,y = fn()
x
out>3
y
out>4
#return可以通过解构获取多个返回值


#return不要跟print混为一谈,print不可用结束返回体,不可用封包,仅仅只是打印作用,
#return是返回,Ipython中out后都是return出来的

函数嵌套

之前函数定义时说过,函数体里面可以包含任意结构,当然也可以包含函数

1
2
3
4
5
6
7
8
9
#实例
def outter():
def inner():
print('inner')
print('outter') #上面inner定义结束,没被调用,先打印outter
inner() #现在在调用inner里面的函数体,才打印inner
outter()
'outter'
'inner'

函数作用域

作用于是一个变量的可见范围,叫做这个变量的作用于。规则就是LEGB

LEGB(Local,Enclosing,Glob,Builtin):局部本地域,父级域,全局域,系统内置

优先级顺序:局部本地域 > 父级域 > 全局域 > 系统内置

作用域可通过:global进行转换

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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
#例子1:
a = 1
def inc():
a = 2
print(x)
inc() #打印22
a
out>1

#例子1.1:
def inc():
b = 2
print(b)
inc() #打印2
b
out>NameError: name 'b' is not defined
#这里函数体里面定义了a,只针对函数体里生效,不影响函数体之外的


#例子2:
def fn():
c = 3
print(c)
def fn1():
print(c)
fn1()
fn() #打印3,3
#函数体内父级作用域对下级域可见。


#例子3:
def fn():
e = 1
print(e)
def inner():
e = 2 #这里重新定义了,定了就是赋值
print(e)
inner()
print(e)
fn() #打印1,2,1
#说明函数体内父级作用于对下级域 “只读可见“ 的关系


#关于“只读可见“在看个例子
x = 1
def inc()
print(x)
inc() #打印1

x=1
def inc():
x = 2
x += 1
print(x)
inc() #打印3

x = 1
def inc():
x = x + 1
print(x)
inc()
UnboundlocalError
#为什么会报错,这里只是上级对下级只读可见,但不能直接修改,除非重新定义
x = 1
def inc(x):
x = x + 1
print(x)
inc(x)
#可能会有人问这为什么正常?这里只是将x传入这个函数里面运算。



#实例4:
x=3
y=4 #这里是全局作用域变量
def swap(x,y):
x,y = y,x #这里是局部变量
print(x)
print(y)
swap(1,2) #打印2,1
x
out>3
y
out>4
#不同作用域变量不可变,但是下级作用域可以对上级作用的变量只读可见,有什么方法对全局可见呢,可使用global


#------------------------global------------------------
#gobal作用是:将局部变量转换成全局变量
#通过global关键字进行修改
#实例
x = 3
def fn():
global x
x += 1
print(x)
fn() #打印4
#global关键字可以提升变量作用域,为全局变量

def fn():
global y
y = 100
print(y)
fn() #打印100
yy
out>100
#global不管外层有没有定义都可以提升

def fn():
global y
fn() #返回空
y
NameError:'y'is not defined
#global只是提升,并没有定义变量,

def fn():
global zz
zz = 2
zz
#这里直接使用zz,并没有调用fn()函数

#通常不建议在函数体内使用全局变量,在后期开发除非你很清楚global会带来什么。除了global没有其他方法的时候才使用global
#还有这里的全局变量和全局作用域是两回事。
#函数之外都是全局作用域,在函数之外定义的变量都是全局变量


#------------------------locals------------------------
#locals用来列出当前作用于范围内的所有变量
#locals()在函数 外部执行 会显示全局作用域
#locals()在函数 内部执行 会显示局部作用域

def fn():
x=1
print(locals())
fn() #打印{'x':1}

def fn(x):
x=1
print(locals())
fn(3) #打印{'x':1}
#这里只是显示函数体内定义的变量

小结

函数之外 在全局作用域

参数列表里面:参数列表作用域是在全局作用域里面,但是只能在函数作用域里面,只能在函数作用域内部用,因为在全局作用域不知道名字叫什么

总结

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
32
33
34
35
#函数的定义
参数
普通参数
默认参数
可变参数(可变位置参数,可变关键字参数)
keyword-only参数

需要注意的是:
普通参数在前面
默认参数在后面
可变参数在后面
可变参数尽量不和默认参数同时出现


函数调用
位置参数
关键字参数
参数解构(位置参数解构,关键字参数解构)


函数返回值
return个数
单个return语句
多个return语句
没有return语句

返回值个数
单值
多值(元祖封包返回多值)
无值(return None)

函数作用域:
作用域遵循LEGB规则
优先级顺序:局部本地域 > 父级域 > 全局域 > 系统内置
提升作用域使用global
看完了?赏个鸡腿钱,谢谢老板!