1 # 函数的引入:
2 # 在程序中,具备“某一功能”的工具就是函数,
3 # “事先准备工具”的过程就是函数的定义
4 # “拿来就用”就是函数的定义
5
6 # 定义函数:
7 # 函数的使用必须遵从先定义后使用的原则
8 # 函数体为pass代表什么都不做,称之为空函数。
9
10 # 函数的使用分为定义阶段和使用阶段,定义阶段只检测语法
11 # 不执行函数体代码,函数名加括号即函数调用,只有调用时才会执行函数体代码
12
13 def foo():
14 print('foo函数')
15 bar()
16
17
18 def bar():
19 print('bar函数')
20
21
22 foo()
23 '''
24 # 解释by李基鹏:foo函数内调用了bar函数,但是此时bar函数还未加载到内存,未报错,说明定义阶段只检测语法,不执行函数内的代码。
25 # 解释by林海峰:定义阶段函数foo和bar均无语法错误,而在调用阶段调用foo函数时,函数foo和bar都早已经存在于内存中了,所以不会出现任何问题。
26 result:
27 foo函数
28 bar函数
29 '''
30
31
32 # 调用有参函数时,实参(变量值)会赋值给形参(变量名),在函数调用时生效,函数调用结束后解除绑定关系
33
34 def register(name, age, sex='男'): # 默认参数必须在位置参数后面
35 return 'name:%s age:%s sex:%s' % (name, age, sex)
36
37
38 # register() TypeError: register() missing 3 required positional arguments: 'name', 'age', and 'sex'
39 print(register('李基鹏', 31, '男'))
40 print(register(age=31, sex='男', name='李基鹏')) # 关键字实参
41 # print(register(sex='男',18,name='李基鹏')) # 位置实参必须在关键字实参前面
42 print(register('李基鹏', age=31, sex='男')) # 位置实参必须在关键字实参前面
43
44 print(register('egon', 18)) # 带默认参数函数的调用
45
46 # 默认参数的值仅在定义阶段被赋值一次
47 x = 1
48
49
50 def foo(arg=x):
51 print(arg)
52
53
54 x = 5
55 foo() # 定义阶段arg的值被赋值成1,此处没有传值相当于用的默认值1
56
57
58 # 默认参数通常为不可变类型
59 # def foo(n, arg=[]): # 此处arg是列表可变类型,应该修改为不可变类型
60 # arg.append(n)
61 # return arg
62
63
64 def foo(n, arg=None):
65 if arg is None:
66 arg = []
67 arg.append(n)
68 return arg
69
70
71 print(foo(1))
72 print(foo(2))
73 print(foo(3))
74
75
76 # 参数的长度可变指的是在调用函数时,实参的个数可以不固定,而在调用函数时,实参的定义无非是按位置或者按关键字两种形式
77 # 这就要求形参提供两种解决方案来分别处理两种形式的可变长度的参数。
78
79 def foo(x, y, z, *args):
80 print(x)
81 print(y)
82 print(z)
83 print(args) # (4, 5, 6, 7)
84
85
86 foo(1, 2, 3, 4, 5, 6, 7) # 1、2、3按照位置参数传给x、y、z,4,5,6,7以元祖的形式传给args
87
88 L = [4, 5, 6, 7] # L为列表形式
89 foo(1, 2, 3, *L)
90 L = (4, 5, 6, 7) # L为元组形式
91 foo(1, 2, 3, *L)
92
93 # 传入时L未加*号,相当于是作为元组的第一个元素传入了
94 L1 = (8, 9, 10)
95 foo(1, 2, 3, L, L1) # L为元组的第一个元素,L1为元组的第二个元素
96
97 '''
98 1
99 2
100 3
101 ((4, 5, 6, 7),)
102 '''
103 foo(1, 2, 3, L, *L1) # 相当于L作为元组的第一个元素,L1里面的8、9、10作为元组的第二个、第三个、第四个元素
104
105 '''
106 1
107 2
108 3
109 ((4, 5, 6, 7), 8, 9, 10)
110 '''
111
112
113 # 如果形参为常规的参数(位置或默认),实参仍然可以是*的形式,我理解为就是打散后传给形参
114 def foo1(x, y, z=3):
115 print(x)
116 print(y)
117 print(z)
118
119
120 foo1(*(1, 2)) # 相当于是将1、2打散后传给x、y
121
122
123 # 求多个数字的和(个数不定)
124 def foo(*args):
125 res = 0
126 for i in args:
127 res += i
128 return res
129
130
131 print(foo(1, 2, 3, 4))
132 print(foo(1, 2, 3))
133
134
135 # 可变长度的关键字参数
136 # 如果在最后一个形参名前加**后,那么在函数调用时,溢出的关键字参数都会被接收,以字典的形式保存下来赋值给该形参
137
138 def foo(x, **kwargs):
139 print(x)
140 print(kwargs)
141
142
143 foo(x=1, y=2, z=3)
144 '''
145 1
146 {'y': 2, 'z': 3}
147 '''
148 foo(y=2, z=3, x=1)
149
150 '''
151 1
152 {'y': 2, 'z': 3}
153 '''
154
155
156 # 如果我们事先生成了一个字典,也是可以传值给**kwargs
157 def foo(x, y, **kwargs):
158 print(x)
159 print(y)
160 print(kwargs)
161
162
163 dic = {'a': 1, 'b': 2}
164 # 字典的key必须是字符串类型
165 # key和value之间是:
166 foo(3, 4, **dic)
167
168
169 # 如果在传入dic时,没有加**,那dic就只能是一个普通的位置参数了
170 # foo(3, 4, dic)
171
172
173 # foo() takes 2 positional arguments but 3 were given,相当于多给了一个位置参数
174
175 # 如果形参是常规参数(位置参数或者默认参数),实参仍然可以是**的形式
176 def foo(x, y, z=3):
177 print(x)
178 print(y)
179 print(z)
180
181
182 foo(**{'x': 1, 'y': 2})
183
184
185 # 如果我们要编写一个用户认证的函数,最开始可能只需要用户名密码就可以了,**kwargs可以为该函数提供扩展,同时保持了代码的简洁性
186 # **kwargs是未命名的关键字参数
187 def register(name, age, **kwargs):
188 if 'sex' in kwargs:
189 # 如果字典内有sex属性执行代码
190 pass
191
192 if 'height' in kwargs:
193 # 如果字典内有height属性执行代码
194 pass
195
196
197 # 想要限定函数的调用者必须用key=value的形式传值,python3提供了专门的语法
198 # 需要在定义形参时用*作为分隔符号,*之后的形参被称为命名关键字参数
199 # 对于这类参数,在函数调用时实参必须以key=value的格式传参,且必须传参
200 def register(name, age, *, sex, height):
201 pass
202
203
204 # register('李基鹏', 31, sex='男') # TypeError:register()缺少1个只需要关键字的参数:“height”
205 # register('李基鹏', 31, sex='男', height=181, width=18) # TypeError:register()收到意外的关键字参数“width”
206 register('李基鹏', 31, sex='男', height=181) # 正确调用
207
208
209 # register('李基鹏', 31, '男', height=181) # TypeError:register()接受2个位置参数,但给出了3个位置参数(和1个仅关键字参数)
210
211
212 # 命名关键字也可以有默认值,从而简化调用
213 def register(name, age, *args, sex='male', height):
214 print('name:%s age:%s sex:%s height:%s' % (name, age, sex, height))
215
216
217 # sex不是默认参数,height也不是位置参数,因为他两都在*后,所以是命名关键字参数
218 # 'male'属于命名关键字参数sex的默认值,因此放在height之前也没问题
219 # 如果形参中已经有一个args了,就不需要单独的*作为分割了
220
221
222 register('lili', 28, height=181) # 正确调用
223 register('gege', 30, sex='female', height=191) # 正确调用
224 register('egon', 18, 'python', '自动化', height=190)
225
226
227 # 所有参数都可以任意组合使用,但顺序必须是位置参数>默认参数>*args或者*>命名关键字参数>**kwargs
228 def test(x, y, z=3, *args, sex='male', height, **kwargs):
229 print(x)
230 print(y)
231 print(z)
232 print(args)
233 print(sex)
234 print(height)
235 print(kwargs)
236
237
238 test(1, 2, 5, 6, 7, sex='female', height=190, hobby='eat')
239 '''
240 1
241 2
242 5
243 (6, 7)
244 female
245 190
246 {'hobby': 'eat'}
247 '''
248
249
250 # 可变参数*args和关键字参数**kwargs通常是一起组合使用的,如果一个函数的形参为*args和**kwargs,那么这个函数是可以接收任意形式、任意长度的参数。
251
252
253 def func(x, y, z):
254 print(x)
255 print(y)
256 print(z)
257
258
259 def warpper(*args, **kwargs):
260 func(*args, **kwargs)
261
262
263 # warpper(1, 2, y = 3) # func()为参数“y”获取了多个值
264 # warpper(1, 2, z=3)
265 # warpper(x=1, y=2, z=3)
266 warpper(1, z=3, y=2)
267
268 # warpper(1, z=3, y=2)相当于是将1被*接收赋值给了args以元组的形式保存下来,z=3、y=2被**接收赋值给了kwargs以字典的形式保存下来
269 # 即args = (1,)
270 # kwargs = {z:3,y:2}
271 # 调用func(*args,**kwargs)即func(*(1,),**{z:3,y:2})
272 # 等同于func(1,z=3,y=2),所以打印结果为123
1 import requests
2
3 # 在程序执行期间,最多存在3种名称空间
4 # x = 3
5 # print(x)
6 # del x # 删除x的引用
7 # # print(x)
8
9 # 内建名称空间:
10 # 生命周期:伴随着python解释器的启动而启动,关闭而回收
11 # 是第一个被加载的名称空间,用来存放一些内置的名字
12 # print(max) # <built-in function max>
13
14 # 全局名称空间:
15 # 生命周期:伴随着python文件的执行而存在,执行结束而回收
16 # 是第二个被加载的名称空间,文件执行过程中产生的名字都会存在于该名称空间中
17
18 # 局部名称空间:
19 # 生命周期:伴随着函数的调用而产生,调用结束而回收
20 # 是第三个被加载的名称空间,函数的形参、函数内定义的名字都会存放于该名称空间中
21
22 # 按照名字作用范围的不同,可以将三个名称空间划分为两个区域。
23 x = 100
24
25
26 # locals():查看局部作用域的名字
27 # globals():查看全局作用域的名字
28 def foo():
29 x = 300
30 print(x)
31 print(locals())
32 print(globals())
33
34
35 foo()
36 print(locals()) # 全局作用域查看locals()等于globals()
37 print(globals())
38 x = 1
39
40
41 def outer():
42 x = 2
43
44 def inner(): # 函数名inner属于outer这一层作用域的名字,函数未调用时不执行函数体代码,切记切记切记
45 x = 3
46 print('inner x:%s' % x)
47
48 inner()
49 print('outer x:%s' % x)
50
51
52 print('全局 x:%s' % x)
53
54 outer()
55
56 # 若要在局部作用域内修改全局名称空间的值,则需要用到global关键字
57 x = 1
58
59
60 def foo():
61 global x # 声明x为全局的
62 x = 3
63 x = 2
64 x = 1
65 print(x) # 这里打印的x也是全局的
66
67
68 foo()
69 print(x)
70
71 # 当实参的值为可变类型时,函数体内对该值的修改,将直接反映到原值
72
73 num_list = [1, 2, 3]
74
75
76 def foo(nums):
77 nums.append(5)
78
79
80 foo(num_list)
81 print(num_list)
82
83
84 # 对于嵌套多层的函数,使用nonlocal关键字可以将名字声明为来自外部嵌套函数定义的作用域(非全局)
85
86 def f1():
87 x = 1
88
89 def f2():
90 nonlocal x
91 x = 2
92
93 f2() # 调用f2其实是修改的f1作用域中x的值
94 print(x) # 打印x,打印的是修改后的x的值
95
96
97 f1()
98
99
100 # 函数对象指的是函数可以被当做数据来处理,具体可分为四个方面的使用
101
102 def add(a, b):
103 return a + b
104
105
106 func = add # 1、函数可以被引用
107 print(func(1, 2))
108
109
110 # 2、函数可以作为容器类型的元素
111 def max(a, b):
112 return a if a > b else b
113
114
115 dic = {'add': add, 'max': max}
116 print(dic['add'](3, 4))
117 print(dic['max'](5, 6))
118
119
120 # 3、函数可以作为参数传入另外一个函数
121
122 def foo(a, b, xxx):
123 return add(a, b)
124
125
126 print(foo(10, 20, add))
127
128
129 # 4、函数的返回值可以是一个函数
130
131 def bar():
132 return add
133
134
135 print(bar()(20, 30))
136
137 # 闭包函数
138 # 基于函数对象的概念,可以将函数返回到任意位置去调用,但作用域的关系是在定义完函数时就已经被确定
139 # 了的,与函数的调用位置无关。
140 x = 1
141
142
143 def f1():
144 def f2():
145 print('x = ' + str(x))
146
147 return f2
148
149
150 def f3():
151 x = 3
152 f2 = f1()
153 f2()
154
155
156 f3()
157
158 x = 1
159
160
161 def outer():
162 x = 2
163
164 def inner():
165 print(x)
166
167 return inner
168
169
170 func = outer()
171 func()
172
173 print(func.__closure__[0].cell_contents) # 可以查看闭包函数所包裹的外部变量
174
175
176 # 为函数传值的两种方式
177 def get(url):
178 return requests.get(url).text
179
180
181 url = get('https://www.baidu.com')
182 print(url)
183
184
185 def page(url):
186 def get():
187 return requests.get(url).text
188
189 return get
190
191
192 message = page('https://www.baidu.com')
193 print(message())