疫情环境下的网络学习笔记 python 3.20

3.20

上节课复习

  1. 名称空间:存放名字的地方

    名字存在栈区,值存在堆区,名称空间是对栈区的一种划分,真正存在的是栈区,名称空间只是一种虚拟的划分

    • 内置名称空间:内置的名字

    • 全局名称空间:只要不是内置的或是函数内的,都在全局里。

      if x == 1:
      	y = 2  # y也是全局
      
    • 局部名称空间:函数内的名字,调用时产生,结束函数就销毁

      def func():  # func名字也是全局,不是函数内的名字
      	a = 1
      	b = 2
      

    运行到定义函数的阶段,不会造出名称空间调用阶段的时候才会

  2. 查找顺序:从当前所外位置,向外查找

  3. 本身名称空间没有嵌套关系,只是有优先级之分

  4. 函数的使用分为定义阶段和调用阶段

    名称空间的嵌套关系以定义阶段为准,无论在哪个阶段调用,查找名字都以定义阶段的位置开始

    在定义阶段就确定了名字要绑定的值的地址

  5. 作用域:名字的作用范围

    全局作用域

    • 包含的名字:内置名称空间,全局名称空间的名字

    • 存活周期:全局存活,全局有效

    局部作用域

    • 函数内的名字,局部名称空间中的名字
    • 临时存活,局部有效
  6. global:声明一个名字,告诉解释器不要在局部创建新的名字,而是去操作全局的名字

    用于在局部修改全局的不可变的名字,也可以在全局新建名字

  7. nonlocal:声明一个名字,从当前层的外一层起始开始找

正课

今日内容

  1. 函数对象:可以把函数当成变量去用

  2. 函数嵌套

  3. 闭包函数 = 名称空间与作用域 + 函数嵌套 + 函数对象

    核心点:名字的查找关系是以函数定义阶段为准的

函数对象

可以把函数当作变量去用

  1. 可以赋值:

    f = func ,使 f 与func指向同一个内存地址:注意不加括号,否则f 被赋值的是函数的返回值

  2. 可以把函数当作参数传入

    def func():
    	pass
    	
    def foo(x):
    	print(x)
    
    foo(func)
    # 得到func的内存地址
    

    实际上是将函数的内存地址当作值传进去了

  3. 可以把函数当作另一个函数的返回值

    def foo(x):  # x = func的内存地址
    	return x  # return func的内存地址
    
    res = foo(func)
    print(res) # res = func的内存地址
    res() # 内存地址+括号,可以调用这个内存地址里的函数
    
  4. 可以当作容器类型的一个元素

    dic = {'k1':func,'k2':func1}
    # 地址的value后面加括号,就可以运行这个函数
    dic['k1']()  # 运行func
    

    用于改进atm项目中,用字典来装一个个函数,其中字典的key为’1‘,’2‘,...,值为func

    def login():
    	pass
    
    def register():
    	pass
    	
    def withdraw():
    	pass
    	
    func_dic = {
    	'1':['登陆',login],
    	'2':['注册',register],
    	'3':['提现',withdraw]
    }
    func_dic['1']()
    

    在打印菜单的时候用循环遍历改进菜单

    while True:
    	for i in func_dic:
    	print(i,func_dic[i][0])
        
    # 这样,加减功能都只用在func_dic 里操作了
    

函数嵌套

嵌套调用:在调用一个函数的过程中又调用其他函数,把一个大的功能拆分成一个个小的功能,组装在一起。且主程序的代码会比较精简

例子:4个值输出最大值

def max2(x,y):
	if x > y:
		return x
	else:
        return y
	
def max4(a,b,c,d):
	res = max2(a,b)
	res1 = max2(res,c)
    res2 = max(res1,d)

result = max4(1,2,3,4)

嵌套定义:如果几个函数的参数都是一个对象,可以把这些函数都定义在一个函数内,使用一个共同的局部名称空间中:封装

例子:求圆的周长 / 面积

def circle(radius,action=0):
    """
    action参数
        0:求周长
        1:求面积
    """
    # 求圆形的周长=2 * pi * radius
    from math import pi

    def perimiter(radius):
        return 2 * pi * radius

    # 求圆形的面积=pi*(radius**2)
    def area(radius):
        return pi * (radius ** 2)

    if action == 0:
        res=perimiter(radius)
    elif action == 1:
        res=area(radius)

    return res

res=circle(3,action=0)
print(res)

闭包函数

很重要

闭包函数 = 名称空间与作用域 + 函数嵌套 + 函数对象

核心点 / 原理:

  • 函数的作用域关系是以函数定义阶段为准
  • 将嵌套函数作为返回值输出可以打破函数层级限制来调用函数

什么是

  1. 定义在函数内部的函数,内嵌函数
  2. 该函数包含对外层函数中名字的引用,(不是对全局作用域)
  3. 通常将闭包函数用 return 返回,使之可以在任意位置使用

return 返回的闭包函数不仅仅有函数本身还带着函数外层的作用域。所以无论在何处调用闭包函数,函数里使用的名字都是定义阶段包裹在其外层的变量

def f1():
	x = 1
	def f2():  # f2是一个闭包函数
		print(x) # 引用了外层的名字x
	f2()
x = 111
f1()
# 无论怎么嵌套,f2都只与他的包相联系

怎么用

可以把闭包函数的使用看作另一种传参的方式

需求:想在任意阶段调用一个内嵌函数

def f1():
    x = 3333
    def f2():
        print(x)
    f2()
    return f2  # f1返回f2函数的内存地址

f = f1  # 于是f在全局得到了局部函数的内存地址
x = 22222
f()  # 因为只与定义阶段的作用域关系有关,所以无论怎么定义x,实际上运算的x只会是f2定义时的 3333
# 3333

应用:request模块得到网页的内容,有两种方式

# 传参方案1,形参中放url
import request
def get(url):
    res = request.get(url)
    print(res.text)
    
get('https://www.baidu.com')

这样定义的时候比较方便,而在要获取不同网页内容的时候需要传入不同的url

# 传参方案2,使用闭包函数
import request
def outter(url):
	# url = 'https://baidu.com'
    def get(url):
        res = request.get(url)
        print(res.text)
    return get
		
baidu = outter('https://www.baidu.com')
baidu()

这样做定义的时候麻烦一些,不过outter的返回值是get函数,调用的时候使用一次 baidu = outter(url) ,那么baidu就相当于一个函数,再使用的时候就可以直接用 baidu()

预习

装饰器

  1. 开放封闭原则:对扩展开放,对修改封闭

    需要一种解决方案,在不修改源代码及调用方式的前提下为函数添加新功能

  2. 装饰器:

    • 器:任意可调用对象(就是函数),装饰与被装饰的对象都是函数
    • 目的:在遵循开放封闭原则的前提为函数新增功能
  3. 例子

    index() 函数,延迟3秒后,打印一行字

    import time
    def index():
    	time.sleep(3)
    	print('index')
    

    现有一需求,计算index函数的运行时间,要求不改变源代码,且不改变调用方式

    def timer(func):  # 创建一个闭包函数的包,传入要装饰的函数
    	def inner():  # 闭包函数中实现新功能
    		start = time.time
    		func()  # 引用外层函数中的名字 func,func是作为形参传入的index 函数
    		stop = time.time
    	return inner
    
    index  = timer(index)  # 传入参数后给闭包函数的包改名
    index()  #因为闭包函数使用的名字永远是在其包中的名字,所以现在无论在哪调用都是新功能,执行index实际上是执行了inner
    

    所以装饰器其实是闭包函数的一种应用场景

posted @ 2020-03-20 15:51  黑猫警长紧张  阅读(201)  评论(0)    收藏  举报