Python学习--Day 14

100.函数嵌套调用

  • 这节课是一节习题课,内容上没什么新知识。
  • 有一个要注意区分的地方,range()是包前不包后,而randint()是包前也包后。

101.全局变量和局部变量

  • 局部变量:仅限于在函数内部使用。
 def func():
 	s = 'abcd'
 	s += 'X'
 	print(s)
 print(s) # 本次调用会报错,出了def范围,系统不认识s
  • 全局变量:声明再函数外部的变量,所有函数都可以访问
name = 'Amy'
def func():
	print(name)
def func1():
	print(name)
  • 局部变量与全局变量同名,使用函数的时候优先使用局部变量,函数没有权限更改全局变量
  • 如果想改,要使用关键字global添加的位置在函数开头,不修改全局变量、只是获取全局变量的话是没有必要使用global的。
name = 'Amy'
def func():
	global name
	name += ' is girl'
	print(name)
func()

102.全局变量是列表类型

  • 关于标题:不是说全局变量的数据类型都是列表类型,这个标题的含义是全局变量是列表类型的时候将要发生什么,比如不需要使用globl
  • 结合上节课的内容,加global的全局变量要求是不可变类型的,而诸如list之类的可变全局变量不需要使用global就可以进行修改
name = 'Amy'
list1 = [1,2,3,4,5]
def func():
	name = 'Tom'
	print(name)
def func1():
	global name 
	print(name)
	name += ' is human'
	list1.appand(8)
	print(list1) # 这样是可以修改list的,因为list是可变类型,不用global也可以修改

103.回顾-1

  • 函数的回顾:普通参数、可变参数(*args、**kwargs)、关键字参数、返回值、函数之间也可以进行调用。
  • 局部变量和全局变量:可变的直接操作,不可变的要声明global才能修改全局。

104.内部函数-2

  • 内部函数:在函数里面声明的一个函数
    def inner_func():,声明后如何调用?可以在整体函数内部调用。
    注意:内部函数可以访问所有变量,可以修改外部函数的可变类型的局部变量;函数内的内部函数想改变函数内的局部变量要使用nonlocal关键字。
name = 'huijia'
list2 = [1,2,3]
def func():
    p = 100  # 局部变量
    list1 = [1, 2, 3, 4, 5]  # 局部变量
    # 声明内部函数
    def inner_func():
    	nonlocal n 
        print('huijia')
        list2.append(3)
        for i in list1:
            i += 5
    inner_func()
func()
print(list2)

105.内部函数的变量访问-3

  • 代码示例如下,内部函数的变量引用,何处加global和nonlocal见下文示例
a = 100
def func():
    # global a 不能在这个地方添加
    b = 99
    def inner_func():
        global a # 为了修改a
        nonlocal b # 为了修改b
        c = 88
        c += 1 # 修改c
        b += 1
        a += 1
        # 尝试打印abc
        print(a,b,c)
    inner_func()
func()

- 总结:可以访问外部函数的变量,内部函数可以修改外部函数的可变类型的变量(如list),内部函数修改全局变量的不可变变量的时候,需要在内部函数声明global 变量名,外部函数修改外部函数的不可变的变量是,在内部函数中声明nonlocal 变量名

  • 使用locals()内置函数进行查看,查看本函数声明的内容有什么:
    注意locals()调用后的结果是什么:由key:value的形式组成
a = 100
def func():
    b = 99
    def inner_func():
        print(a,b)
    inner_func()
    print(locals())
func()
# 输出
# 100 99
# {'inner_func': <function func.<locals>.inner_func at 0x0000028FBE04F158>, 'b': 99}
  • 还可以使用globals()查看全局内有什么,带__的都是系统自动加载的。
a = 100
print(globals())
def func():
    b = 99
    def inner_func():
        print(a,b)
    inner_func()
func()
# 输出:
# {'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': 
# <_frozen_importlib_external.SourceFileLoader object at 0x000001E5BC5B4F98>, '__spec__': None, # '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, '__file__': 
# 'C:/Users/10727/PycharmProjects/untitled/func01.py', '__cached__': None, 'a': 100}
# 100 99
  • locals()查看本地变量有哪些,以字典的形式输出。
  • globals()查看全局变量有哪些,以字典的形式输出,里面会有一些系统的键值对。

106.闭包-4

  • 闭包的概念:是在函数中提出的概念,要符合以下几个条件:①在外部函数中定义了一个内部函数;②外部函数是有返回值的;③返回的值是:内部函数的名称(即没有括号);④内部函数引用了外部函数的变量。格式:
 def 外部函数():
 	...
 	def 内部函数():
 		...
 	return 内部函数 
 	# 注意没有括号
  • 想访问函数内的内部变量,怎么办?可以使用return讲对应的变量返回,因此我们也想使用return返回对应的内置函数。如下代码是一个闭包的例子:
def func():
    a = 100
    def inner_func():
        b = 99
        print(a,b)
    return inner_func #记住return的是inner_func的名称,而不是带括号的调用

x = func() # 现在x就是inner_func,现在的x()就是调用
x()
  • 闭包实战:
 def func(a,b):
 	c = 10
 	def inner_func():
 		s = a + b + c
 		print('相加之后的结果是:'s)
 	return inner_func
 
 func(6,9)()
 # x = func(6,9)  
 # x()

107.闭包保存参数的状态-5

  • 闭包保存参数的状态:内部函数在外部函数被调用的时候才分配内存,所以内次的地址都是不一样的,换句话说,外部函数调用两次,内部函数就会声明两次,因此返回了内部函数的地址,在执行完不会被释放,获得了保存状态的能力。【每次调用函数,系统都会为函数提供新的内存。
    内部函数会不会因为多次调用而被覆盖?不会,详见下面代码的测试:
def func(a,b):
    c = 10
    def inner_func():
        s = a+b+c
        print(s)
    return inner_func
ifunc = func(6,9)
ifunc1 = func(2,8)
ifunc1()
# 问这时候执行下面的代码会不会因为ifunc1受到影响?
ifunc()
# 结果:20 25

108.闭包之计数器-6

  • 对比上一节课程中使用的int类型,本节课使用了list,即可变类型。
    可变类型是可以进行修改的,每次都会重复调用,但是int不会被修改,每次都是访问一个全新的。
    是一个对比,关键在于了解可变类型与不可变类型的区别,并因此导致了内置函数调用的时候产生的不同结果。
def generate_count():
    container = [0]
    def addone():
        container[0] = container[0] + 1
        print('当前是第{}次访问'.format(container[0]))
    return addone
counter = generate_count()
counter()
counter()
counter()
# 输出:
# 当前是第1次访问
# 当前是第2次访问
# 当前是第3次访问

109.闭包同级访问-7

  • 闭包的缺点总结:作用域不直观,因为变量不会被回收所以有一定的内存占用问题

  • 闭包的作用:可以使用同级的作用域、读取其他元素的内部变量、延长作用域

  • 闭包总结:
    ①闭包优化了变量,原来需要类对象完成的工作,闭包也可以完成;
    ②由于闭包引用了外部函数的局部变量,则外部函数的局部变量没有及时释放,消耗内存;
    ③闭包的好处,使代码变得简介,便于阅读代码
    ④闭包是理解装饰器的基础

  • 同级访问示例:

def func():
    a = 100
    def inner_func1():
        b = 90
        s = a + b
        print(s)
    def inner_func2():
        inner_func1() # 同级调用
        print('---func2')
    return inner_func2
f = func()
f()
print(f)
# 190
# ---func2
# <function func.<locals>.inner_func2 at 0x000001615C74F1E0>
  • 内部函数再return的情况
def func():
    a = 100
    def inner_func1():
        b = 90
        s = a + b
        print(s)
    def inner_func2():
        inner_func1() # 同级调用
        print('---func2')
        return 'hello' # 再return
    return inner_func2
f = func()
ff = f()
print(ff)
# 输出
# 190
# ---func2
# hello

110.装饰器-8

  • 引入(注意外部函数传入参数在内部函数的修改方式,要声明nonlocal
def func(number):
	a = 100
	def inner_func():
		nonlocal a
		nonlocal number # 对上面外部函数的传入参数进行修改,也要使用nonlocal
		number += 1 #对传入的参数进行修改
		for i in range(number):
			a += 1a
		print('修改后的a',a)
	return inner_func
f = func(5)
  • 地址引用赋值?返回值与test()一样,证明函数也可以被“变量化”
a = 10 # 声明整型变量
b = a # 赋值
def test(): #声明函数
	print('执行代码')
t = test #赋值?
t()
# 输出结果
# 执行代码
  • 将函数作为参数传输
def test(): #声明函数
	print('执行代码')
def func(f):
 	print(f)
 	f()
func(test) # 将函数作为参数传递
# 执行结果:
# <function test at 0x00000177886C2EA0>
# 执行代码
  • 装饰器:函数作为参数出现,要有闭包的特点
  • 定义一个装饰器(decorate,装饰):使用@标记,与Java的重写类似。
  • 老师的引入:如果想在不改变一个很多其他代码都用的函数的基础上,希望仅仅调用该函数就能够产生不局限于原代码的特定效果,该怎么办?
    方式1:(笨重的方法,对多个函数都要装饰的时候是无效的)
 def house():
 	print('毛坯房')
 def house1():
 	house()
 	print('刷漆')
 	print('铺地板')
  • 方式2:使用装饰器
def decorate(func):
    def wrapper():
        func()
        print('装修')
    return wrapper
@decorate
def house():
    print('hello')
house()
# 输出函数:
# hello
# 装修

111.装饰器参数(万能装饰器)-9

  • 装饰器实战:仅如下代码也会有输出(即并没有调用,也有了输出)
    在底层,具体进行过程是下面的:①house()是被装饰函数②将被修饰函数作为参数传给装饰器decorate;③执行decorate函数;④将返回值赋给house,也就是被修饰函数可以见下图,我自己又写了一遍老师讲的流程。
    其中decorate的return语句也执行了,返回的wrapper被house接收。
    在这里插入图片描述
def decorate(func):
    a = 100
    print('wrapper外层打印测试')
    def wrapper():
        func()
        print('---刷漆')
        print('---装修')
    print('wrapper加载完成')
    return wrapper

@decorate # 使用装饰器
def house():
    print('hello')
# wrapper外层打印测试
# wrapper加载完成
  • 装饰器实战:
    使用装饰器对方法进行重写。
import time
def decorate(func):
    def wrapper():
        print('正在校验中')
        time.sleep(2)
        print('校验完毕')
        # 调用原函数
        func()
	return wrapper
@decorate  # 装饰f1
def f1():
    print('---f1---')
def f2():
    print('---f2---')
f1()
f2()
  • 新问题:如果被装饰的函数有参数怎么办?
    首先明确,在这里的情况下,不能使用f1(5)进行直接调用,因为这里的f1本质上是wrapper函数,该函数在修改前是没有参数的,因此会报错。
    改动上,就在wrapper内置函数上添加参数,并将它传给合适的外部函数。
import time
def decorate(func):
    def wrapper(x): # 在wrapper处进行传参操作
        print('正在校验中')
        time.sleep(2)
        print('校验完毕')
        # 调用原函数
        func(x) # 将x传给对的人
	return wrapper
@decorate  # 装饰f1
def f1(n): # 此时函数需要参数的传入
    print('---f1---',n)
def f2(n):
    print('---f2---',n)
f1(3)
f2(3)
# 输出结果
# 正在校验中
# 校验完毕
# ---f1--- 3
# ---f2--- 3
  • 万能装饰器,就是用*args作为形式参数,将对应的部分填充好,具体实战代码如下:
import time
def decorate(func):
    def wrapper(*args):
        print('正在校验中')
        time.sleep(2)
        print('校验完毕')
        # 调用原函数
        func(*args)
    return wrapper
@decorate
def f2(name,age):
    print('---f2---{}{}'.format(name,age))
@decorate
def f3(list1):
    for i in list1:
        print(i)
f2('lili',5)
list1 = [1,2,3]
f3(list1)
  • 启发,既然可以使用*args,那么也能够使用**kwargs接收关键字参数,老师说开发时常用的是下面的万能装饰器
def decorate(func):
    def wrapper(*args,**kwargs):
        print('正在校验中')
        time.sleep(2)
        print('校验完毕')
        # 调用原函数
        func(*args,**kwargs)
    return wrapper

112.多层装饰器-10

  • 多层装饰器:在函数外放两个装饰函数,离谁近先执行谁,因为装饰器默认装饰下面的东西,此处是装饰器1装饰2+函数,因此实际上是等待装饰器2装饰完函数,再用1装饰函数+装饰器2。
def zhuang1(func):
	print('---1.1 start')
	def wrapper(*args,**kwargs):
		func()
		print('刷油漆')
	print('---1.2 end')
	return wrapper
	
def zhuang2(func):
	print('---2.1 start')
	def wrapper(*args,**kwargs):
		func()
		print('装门')
	print('---2.2 end')
	return wrapper
@zhuang1
@zhuang2
def house():
	print('毛坯房')
house()
# 输出:
# ---2.1 start
# ---2.2 end
# ---1.1 start
# ---1.2 end
# 毛坯房
# 装门
# 刷油漆
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值