Python学习笔记:1.4.3 嵌套函数和装饰器
程序员文章站
2024-01-15 19:51:40
...
本文是学习齐老师的《python全栈工程师》课程的笔记,欢迎学习交流。同时感谢齐老师的精彩传授!
一、课程目标
- 初步理解函数是对象思想
- 掌握嵌套函数的编写方法
- 理解变量作用域
- 了解简单的装饰器及其用法
二、详情解读
1.函数是对象:
- 比较函数和字符串的各种表现
例子:
# 函数也可以作为参数所引用的对象,传给另外一个函数。
def opt(func, iterable):
return [func(i) for i in iterable]
lst = opt(abs, [-1, -2, -3])
print(lst) # [1, 2, 3]
2.嵌套函数:
- 返回函数将函数作为返回值,可以返回内置函数对象,也可以返回自定义函数对象。
例子:
def my_func(bf):
return bf
# 返回内置函数对象 abs
mabs = my_func(abs)
print(abs is mabs) # True 因为它们指向同一个函数对象
print(mabs(-3)) # 3 相当于 abs(-3),
def add(x, y): return x + y
# 返回自定义函数对象
madd = my_func(add)
print(id(madd) == id(add)) # True 等同于 madd is add
print(madd(1,2) # 3
- 函数体内定义另外一个函数:内嵌的函数不会随着外层函数调用而被执行,它也不能在外层函数所在的空间调用,并且,嵌套函数返回内嵌的函数对象,否则内嵌函数无法被调用
例子:
def foo():
def bar():
print('I am in bar')
print('I am in foo.')
foo() # I am in foo.
bar() # 报错,外层函数没有返回内层函数,所以内层函数只能在foo函数内被调用。
def foo():
def bar():
print('I am in bar')
print('I am in foo.')
return bar # 注意,这里不加括号!用bar即可,不用bar()
b = foo()
b() # I am in foo. I am in bar.
3.变量作用域:
作用域指的是变量的有效范围,作用域分为函数内部的局部(本地)作用域、全局作用域、内置作用域。嵌套函数内、嵌套函数之外但在外层函数之内的属于函数内部的局部(本地)作用域。
x = 2000 # 全局作用域
def foo():
x = 1 # 函数内部的局部(本地)作用域
def bar():
y = 2 # 函数内部的局部(本地)作用域
return bar
内置作用域:下面这些内置函数都属于内置作用域范围
注意: 全局作用域的变量不能和内置作用域的变量冲突,否则会覆盖掉内置作用域的变量
4.划分作用域:
根据代码结构划分不同级别的作用域:(块级)、函数、类、模块、包。Python中,if语句块、for语句块、with上下文管理器等等不能划分变量作用域。函数和类改变作用域:def、class、lambda
if True:
x = 1
print(x) # 1 因为if语句不能划分作用域,此时x变量是全局变量
def func(): a = 8
print(a) # 报错 因为a在函数func内,属于局部作用域,外部无法调用
5.python解析器搜索变量的规则:
- 从内向外,逐层搜索,找到则停止。
- 作用域取决于代码块在整体代码中的位置,例如函数代码块的位置,而与函数调用的位置无关
6.变量掩盖和修改:
- 如果在函数内部引用了一个和全局变量同名的变量,且不是重新赋值,那函数内部引用的是全局变量。
- 如果函数内部重新赋值了一个和全局变量名称相同的变量,则这个变量是局部变量,它会掩盖全局变量。
x = 3
def g(): print(x)
g() # 3
def g2():
x = 2
print(x) # 此时的 x 覆盖了全局变量中的 x
g() # 2
print(x) # 3 这里的x 是全局变量中的 x
两个关键词:
- global: 指定当前变量使用外部的全局变量。
- nonlocal: 内层函数中的变量使用它外一层函数的变量。
– global修饰的变量可能事先并未存在于全局作用域内,但nonlocal修饰的变量必须已经存在于外层函数,不能只存在于全局。
例子:
x = 1
def foo():
x += 2
print(x)
foo() # 报错 函数中的 x += 2,而在函数内 x 并未声明,如果想引用全局变量,必须在函数内用global关键字声明x
# 比如下面这个例子:是正确。
y = 2
def bar():
global y
y += 3
print(y)
bar() # 5
x = 4
def foo():
x = 5
def bar():
y = 6
print(x, y)
return bar
b = foo()
b() # 5 6
------------------
x = 6
def foo():
x = 4
def bar():
x = x + 1 # 此处的 x 被重新赋值,所以它是bar函数里的局部变量,而在函数 bar 内并未声明 x
print(x)
return bar
b = foo()
b() # 报错
# 下面的例子是正确的
x = 6
def foo():
x = 4
def bar():
# 在局部中声明变量 x,这里x=4,当在嵌套函数中想引用外层函数的变量时用nonlocal
# 此处如果改为 global x,则x引用的是全局变量x=6
nonlocal x
x = x + 1
print(x)
return bar
b = foo()
b() # 5
7.例题讲解:
例题1:
- 利用嵌套函数,编写实现”一元二次函数“的程序
老师参考:
def parabola(a, b, c):
def para(x):
return a * x**2 + b * x + c
return para
y = parabola(2, 3, 4)
r = y(3)
print('y = 2x^2 + 3x + 4 | x = 3, the result = {0}'.format(r))
运行效果图:
8.简单的装饰器:
- 从嵌套函数到语法糖@
- 没有参数的装饰器
def book(name):
return name
def strong_dec(f):
def wrapper(name):
return '<strong>{0}</strong>'.format(f(name))
return wrapper
my = strong_dec(book)
book = my('Learn Python')
print(book)
# 下面用语法糖 @ 符号
def strong_dec(f):
def wrapper(name):
return '<strong>{0}</strong>'.format(f(name))
return wrapper
# my = strong_dec(book)
# book = my('Learn Python')
@strong_dec
def book(name):
return name
b = book('Learn Python')
print(b)
例题2:
- 编写一个用于测试函数执行时间的装饰器
def timing_func(func):
def wrapper():
start = time.time()
func()
stop = time.time()
return (stop - start)
return wrapper
@timing_func
def test_list_append():
lst = []
for i in range(1, 100000):
lst.append(i)
@timing_func
def test_list_compre():
[i for i in range(0, 100000)]
a = test_list_append()
print(a)
b = test_list_compre()
print(b)
# tip: 这个例子告诉我们,列表推导式比for循环运行速度快。
# 至于为什么,请参考这篇文章:
# 《为什么列表推导式会更快》(https://blog.csdn.net/sinat_38682860/article/details/85159505)
运行效果图:
三、课程小结
- 了解了函数是对象
- 学习了函数和变量的作用域
- 学习了嵌套函数
- 学习了简单的装饰器
四、作业
- 在字典中有 get 方法,但是列表中没有。编写函数,对列表实现类似字典中get方法的功能。
小编参考:
# 待定。。。小编还在思考中。。。>_<