1 2 3 4

python函数2_闭包和装饰器

变量的范围

作用域

  • 优先级:L > E > G > B
  • L,local,当前函数中的变量
  • E,encloseing,父函数中的变量
  • G,Global,全局变量
  • B,built-in,内置函数

局部/全局

  • 局部变量
    • 在函数内部声明的变量
    • 在函数体外部无法获取
  • 全局变量
    • 在函数外部声明的变量
    • 所有函数都可以访问
在函数内,局部变量和全局变量同名,优先使用局部变量
name = '月月'
def fun2():
    name = '小月月'
    name += '会弹吉他'
    print(name)

fun2()
小月月会弹吉他
当在函数体内,尝试更改全局变量会报错
name = '月月'
def fun2():
    print(name)
    name += '会弹吉他'

fun2()
当需要在函数体内修改全局变量时,需要加global

只是获取不需要加

name = '月月'
def fun2():
    global name
    name += '会弹吉他'
    print(name)
fun2()
print(name)
月月会弹吉他
月月会弹吉他
当在函数中要修改的全局变量是可变类型的,如列表,则不需要添加global关键字
list1 = ['python','php','go']
def fun2():
    list1.append('java')
fun2()
print(list1)
['python', 'php', 'go', 'java']

内部函数

可以访问外部函数的变量
a = 10
def func1():
    b = 20
    def func2():
        print(a,b)
    func2()
func1()
10 20
内部函数可以修改外部函数的可变类型的变量比如:list
list1 = ['python','php','go']
def func1():
    list2 = ['html','css']
    def func2():
        list1.append('java')
        list2.append('javascript')
        print(list1,list2,sep='\n')
    func2()
func1()
['python', 'php', 'go', 'java']
['html', 'css', 'javascript']
内部函数修改全局的不可变变量时,需要在内部函数声明global 变量名
a = 10
def func1():
    b = 20
    def func2():
        global a
        a += b
        print(a,b)
    func2()
func1()
内部函数修改外部函数的不可变变量时,需要在内部函数中声明,nonlocal 变量名
a = 10
def func1():
    b = 20
    def func2():
        nonlocal b
        b += a
        print(a,b)
    func2()
func1()
locals(),可以看到当前函数中声明的变量有哪些,以字典的形式输出
a = 10
def func1():
    b = 20
    def func2():
        nonlocal b
        b += a
        print(locals())
    func2()
    print(locals())
func1()
{'b': 30}
{'func2': <function func1.<locals>.func2 at 0x000001BF232F4730>, 'b': 30}
globals(),查看全局变量有那些,以字典的形式输出(会有一些系统中的键值对)
a = 10
def func1():
    b = 20
    def func2():
        nonlocal b
        b += a
        print(globals())
    func2()
func1()

闭包

概念

闭包的条件

  1. 外部函数中定义了内部函数
  2. 外部函数是有返回值
  3. 返回的值是: 内部函数名
  4. 内部函数引用了外部函数的变量

闭包的优缺点

  • 闭包引用了外部函数的局部变量,则外部函数的局部变量没有及时释放,消耗内存
  • 闭包使代码变得简洁,便于阅读代码
def func1():
    a = 10
    def func2():
        b = 20
        print(a,b)
    return func2

new1 = func1()
new1()
10 20
def func(a,b):
    c = 10
    def inner_func():
        s = a + b + c
        print(s)

    return inner_func
ifunc = func(1,2)
ifunc()
13

闭包的简单应用

保存返回闭包时外层函数变量的状态

def func(a,b):
    c = 10
    def inner_func():
        s = a + b + c
        print(s)

    return inner_func
ifunc = func(1,2)
ifunc1 = func(3,4)
ifunc2 = func(5,6)
ifunc()
ifunc1()
ifunc2()
13
17
21

计数器

def generate_count():
    container = [0]
    def add_c():
        container[0] = container[0] + 1
        print('当前是第{}次访问'.format(container[0]))
    return add_c
counter = generate_count()
counter()
counter()
counter()
当前是第1次访问
当前是第2次访问
当前是第3次访问

闭包同级别访问

def func():
    a = 10
    def inner_func1():
        b = 90
        print(a+b)
    def inner_func2():
        inner_func1()
        return 'hello'
    return inner_func2

f1 = func()
f2 = f1()
print(f2)
100
hello

装饰器

装饰器函数中,被装饰函数函数是作为参数出现的
要有闭包的特点

定义使用装饰器

定义一个装饰器

def decorate(func):
    def wrapper():
        func()
        print('--->刷漆')
        print('--->铺地板')
        print('--->装门')
    return wrapper

使用装饰器

@decorate
def house():
    print('我还是毛坯房')

调用函数

house()
我还是毛坯房
--->刷漆
--->铺地板
--->装门

当遇到装饰器,会自动完成

  • 当解释器读取到@decorate时,将house()函数作为被装饰函数
  • 将被装饰函数作为参数传给装饰器decorate
  • 执行decorate函数
  • 将返回值又赋值给house

所以调用house()时,其实调用的是wrapper()

import time

def decorate(f):
    def wrapper():
        print('正在校验中')
        time.sleep(2)
        print('校验完毕')
        f()
    return wrapper

@decorate
def f1():
    print('--f1--')

@decorate
def f2():
    print('--f2--')

f1()
f2()
正在校验中
校验完毕
--f1--
正在校验中
校验完毕
--f2--

函数的参数

import time

def decorate(f):
    def wrapper(*args,**args2):
        print('正在校验中')
        time.sleep(2)
        print('校验完毕')
        f(*args,**args2)
    return wrapper

name1 = 'tom'
list1 = ['tom','json','peop']
kv1 = {'01':'tom','02':'json','03':'peop'}

@decorate
def f1(n1):
    print('--f1--',n1)

@decorate
def f2(n1,l1):
    print('--f2--',n1,l1)

@decorate
def f3(n1,l1,k1):
    print('--f3--',n1,l1,k1)

f1(name1)
f2(name1,list1)
f3(name1,list1,kv1)
正在校验中
校验完毕
--f1-- tom
正在校验中
校验完毕
--f2-- tom ['tom', 'json', 'peop']
正在校验中
校验完毕
--f3-- tom ['tom', 'json', 'peop'] {'01': 'tom', '02': 'json', '03': 'peop'}

多层装饰器

def decorate1(func):
    print('--->one')
    def wrapper(*args,**kwargs):
        func(*args,**kwargs)
        print('刷漆')
    return wrapper

def decorate2(func):
    print('--->two')
    def wrapper(*args,**kwargs):
        func(*args,**kwargs)
        print('铺地板')
    return wrapper


@decorate1
@decorate2
def house():
    print('我是毛坯房')

house()
--->two
--->one
我是毛坯房
铺地板
刷漆
  • 当存在多个装饰器时,先执行距离被装饰函数近的装饰器

    1. 先到@decorate2装饰器中,执行打印two,加载wrapper函数,并将返回值又赋值给house
    2. 然后再到@decorate1装饰器中,执行打印one,加载wrapper函数,并将返回值又赋值给house
  • 当调用house()时

    1. 此时house()为@decorate1中的wrapper()函数
    2. 当执行到func()会跳转到第一层@decorate2中的wrapper()函数
    3. 此函数中的func()函数执行时,会执行house()函数中的内容,打印毛坯房
    4. 接着打印铺地板
    5. 然后回到跳转时的状态,再打印刷漆

装饰器的参数

当装饰器需要带参数时,装饰器使的外层需要再嵌套一层

发现下面装饰器的例子中的装饰器,嵌套了三层
可以理解为

  1. 最外层outer(),负责接收装饰器参数的
  2. decorate(),负责接收函数的
  3. 最内层wrapper(),负责接收函数参数的
def outer(n):
    def decorate(func):
        def wrapper(*args):
            func(*args)
            print('铺{}块地砖'.format(n))
        return wrapper
    return decorate

n1 = 10
n2 = 1200
times = '2020.01.01'

@outer(n1)
def house(time):
    print('我{}拿到房子的钥匙,是毛坯房'.format(time))

@outer(n2)
def street():
    print('新修街道:黑泉路')

house(times)
street()
我2020.01.01拿到房子的钥匙,是毛坯房
铺10块地砖
新修街道:黑泉路
铺1200块地砖

登录验证

import time
islogin = False

def login():
    username = input('请输入用户名: ')
    password = input('输入密码: ')
    if username == 'admin' and password == '123456':
        return True
    else:
        return False

def login_required(func):
    def wrapper(*args,**kwargs):
        global islogin
        print('----付款----')
        while True:
            if islogin:
                func(*args,**kwargs)
                break
            else:
                print('用户没有登录,不能付款')
                islogin = login()
    return wrapper

@login_required
def pay(money):
    print('正在付款,付款金额是:{}元'.format(money))
    print('付款中.')
    time.sleep(1)
    print('付款完成!')

pay(10)
----付款----
用户没有登录,不能付款
请输入用户名: admin
输入密码: 123
用户没有登录,不能付款
请输入用户名: admin
输入密码: 123456
正在付款,付款金额是:10元
付款中.
付款完成!

匿名函数

匿名函数

作用:简化函数定义

格式

lambda 参数:返回的结果
s1 = lambda x,y : x*y
result = s1(2,5)
print(result)
10

匿名函数作为参数

def func(x,y,f):
    s = f(x,y)
    print(s)
func(2,3,lambda a,b : a*b)
6

匿名和内置配合使用

max()

min()最小值也是相同用法

查找列表中最大值
list1 = [1,4,8,2,4,1]
a = max(list1)
print(a)
8
查找列表中a是最大值的字典
list2 = ({'a':10,'b':20},{'a':15,'b':20},{'a':7,'b':20})
a = max(list2,key=lambda x:x['a'])
print(a)
{'a': 15, 'b': 20}

map()

将列表中的每一个元素加2
list1 = [1,4,8,2,4,1]
for index,i in enumerate(list1):
    list1[index]=i+2
print(list1)
[3, 6, 10, 4, 6, 3]
list1 = [1,4,8,2,4,1]
result = map(lambda x:x+2,list1)
print(list(result))
[3, 6, 10, 4, 6, 3]
将列表中的值,奇数+1.
list1 = [1,4,8,2,4,1,5,7]
result = map(lambda x:x if x%2==0 else x+1,list1)
print(list(result))
[2, 4, 8, 2, 4, 2, 6, 8]
list1 = [1,4,8,2,4,1,5,7]
for index,i in enumerate(list1):
    if i%2 != 0:
        list1[index] = i+1
print(list1)
[2, 4, 8, 2, 4, 2, 6, 8]

reduce()

对列表中的元素进行加减乘除运算的函数
使用时,需要导入

from functools import reduce
list1 = [1,4,5,2,3,65,1,4,7,5]
result = reduce(lambda x,y : x+y,list1)
print(result)
97

执行过程,

  1. 先取1,4,进行相加,
  2. 然后将结果和再次取来的5进行相加,以此类推
设置初始值

当列表中只有1个元素时,因为要获取两个值,
但是只能获取一个值,会将另一个值看作是0或者null

list1 = [10]
result1 = reduce(lambda x,y : x-y,list1)
result2 = reduce(lambda x,y : x-y,list1,100)
print(result1,result2)
10 90

filter()

查找列表中小于10的元素
list1 = [12,5,7,5,4,13,100,10]
result = filter(lambda x : x>10, list1)
print(list(result))
[12, 13, 100]
list1 = [12,5,7,5,4,13,100,10]
result = []
for i in list1:
    if i > 10:
        result.append(i)
print(result)
[12, 13, 100]
找出所有年龄大于20岁的学生
students = [
    {'name':'tom','age':25},
    {'name':'jack','age':19},
    {'name':'lily','age':27}
]

result = filter(lambda x : x['age']>20,students)
print(list(result))
[{'name': 'tom', 'age': 25}, {'name': 'lily', 'age': 27}]

sorted()

按照年龄排序
students = [
    {'name':'tom','age':25},
    {'name':'jack','age':19},
    {'name':'lily','age':27}
]
result = sorted(students,key=lambda x : x['age'])
print(result)
result2 = sorted(students,key=lambda x : x['age'],reverse=True)
print(result2)
[{'name': 'jack', 'age': 19}, {'name': 'tom', 'age': 25}, {'name': 'lily', 'age': 27}]
[{'name': 'lily', 'age': 27}, {'name': 'tom', 'age': 25}, {'name': 'jack', 'age': 19}]

递归函数

函数自己调用自己

  • 递归函数一定要设置终点
  • 通常都会有一个入口
def f1(n):
    if n>0:
        print(n)
        f1(n-1)
    else:
        print(n)
f1(5)
5
4
3
2
1
0
posted @ 2020-02-20 22:37  多走多看  阅读(124)  评论(0)    收藏  举报