18. Funciones avanzadas (cierres y decoradores)

1. Función de cierre

1. Conceptos básicos de cierre

Hemos aprendido sobre el anidamiento de funciones antes, veamos un ejemplo sobre el anidamiento de funciones.

La función externa outside_1 define una lista vacía lst, y luego llama a la función interna inner_1, pasando un parámetro 1 a la función interna cada vez que se llama. Cuando se ejecuta inner_1, se agrega un 1 a lst, y luego se pasa lst la función externa Volvió a la función de llamada.

def outer_1():
    lst = []
    def inner_1(num1):
        lst.append(num1)
    inner_1(1)
    return lst
f1 = outer_1()
f2 = outer_1()
print(f1)
print(f2)

# 输出:
[1]
[1]

Descubrimos que el valor de f1 es [1], y el valor de f2 también es [1]. El valor de lst se inicializará en una lista vacía cada vez que se ejecute. ¿Hay alguna manera de que lst siga guardando el elementos almacenados en él?

Las variables locales se liberarán automáticamente en la memoria después de que se ejecute la función.Si queremos que las variables locales duren mucho tiempo, podemos usar funciones de cierre.

A continuación, observe la estructura de la función de cierre.

def outer_1():                 # 动作2 进入outer_1执行过程
    lst = []                   # 动作3 生成一个空列表 
    print("I am outer_1")
    def inner_1(num1):         # 动作4 定义一个名为inner_1的内层函数 # 动作8 将参数1传给num1 # 动作13 将参数1传给num1
        lst.append(num1)       # 动作9 向lst添加元素1   # 动作14 向lst再添加元素1
        return lst             # 动作10 将lst返回       # 动作15 将lst返回
    return inner_1             # 动作5 将函数inner_1返回,此时f1获得inner_1函数
f1 = outer_1()                 # 动作1 调用函数outer_1  # 动作6 将返回的结果赋值给f1
print(f1(1))                   # 动作7 调用函数f1(1)    # 动作11 将返回的lst打印出来
print(f1(1))                   # 动作12 再次函数f1(1)   # 动作16 将返回的lst打印出来

# 输出
I am outer_1
[1]
[1, 1]

print(type(f1)) # 输出 <class 'function'>
print(f1) # 输出 <function outer_1.<locals>.inner_1 at 0x109aeb550>

La función externa define una lista vacía lst, la función interna hace referencia a la variable lst de la función externa, usa append para agregar elementos y devuelve lst, y la última línea de la función externa devuelve el nombre de la función interna como un objeto.

Echemos un vistazo más de cerca a cómo se ejecuta el programa:

Acción 1: f1=outer_1(), ejecute primero la función en el lado derecho del signo igual

Acción 2: Entrar en el proceso de ejecución de outside_1

Acción 3: Producir una lista vacía llamada lst

Acción 4: Definir una función interna llamada inner_1

Acción 5: Devolver la función inner_1

Acción 6: Asigne el resultado devuelto a f1, en este momento f1 ha obtenido la función interna inner_1

Acción 7: Llamar a la función f1(1), pasando un parámetro a inner_1()

Acción 8: Ingrese la función interna directamente, y el parámetro formal num recibe el valor 1

Acción 9: Agregar elemento 1 a lst

Acción 10: devolver lst

Acción 11: Imprimir el lst devuelto

Descubrimos que la función externa solo se ejecuta durante la asignación de f1=outer_1(), y luego f1(1) es solo una llamada a la función interna inner_1. El proceso de ejecutar f1(1) por segunda vez es básicamente similar, excepto que durante la segunda ejecución, lst no se elimina de la memoria y el resultado de la primera ejecución aún se conserva, por lo que después de la segunda ejecución, lst El el valor es [1,1].

Si te sientes un poco mareado al entender este proceso, otra buena manera es depurar el código anterior línea por línea y observar el proceso de ejecución, que es mucho más fácil de entender.

2. Condiciones de formación del cierre

Si una función está anidada dentro de una función y la función interna se refiere a variables en el ámbito externo (ámbito no global), entonces la función interna se denomina cierre. Los cierres recuerdan los valores de las variables en el ámbito externo al que se refieren cada vez que se ejecutan.

Condiciones de formación del cierre:

(1) Una función anida otra función;

(2) La función interna hace referencia a las variables de la función externa;

(3) La función exterior devuelve la función interior como valor de retorno.

3. Variables libres

En el ejemplo anterior, interior_1 hace referencia a la variable lst de la función exterior exterior_1. Una vez que se ejecuta la función exterior exterior_1, interior_1 se convierte en una función de cierre, y la variable lst a la que hace referencia la función interior se denomina variable libre.

La variable libre formará una relación vinculante con la función interna y está asociada con una instancia específica del cierre. Las variables libres referenciadas por cada instancia del cierre no interfieren entre sí. Entendamos esta no interferencia mutua a través de un ejemplo

def outer():
    lst = []
    def inner(num):
        lst.append(num)
        print('lst:', lst)
    return inner
f1 = outer()
f2 = outer()

f2(1)
f1(2)
f2(2)

输出:
lst: [1]
lst: [2]
lst: [1, 2]

En este código, a través de f1 = exterior(), f2 = exterior(), generamos dos instancias de cierre, son f1, f2 respectivamente.

Cuando se ejecuta f2(1), se ejecuta la función de cierre y se pasa un parámetro 1 a internal, lst está vacío en este momento y se le agrega un elemento 1, por lo que el resultado de lst en este momento es [1 ].

Cuando se ejecuta f1(1), se ejecuta la función de cierre y se pasa un parámetro 1 a internal, lst todavía está vacío en este momento y se le agrega un elemento 1, por lo que el resultado de lst en este momento es [ 1].

Cuando se ejecuta f2(2), se ejecuta la función de cierre y se pasa un parámetro 2 a interior, porque f2(1) se ha ejecutado, lst ya es [1], y se le agrega un elemento 2, por lo que el resultado de lst en este momento es [1,2].

A través del proceso anterior, podemos verificar y concluir que lst existe tanto en f1 como en f2, y que no interfieren entre sí.

Además de definirse en la función externa, las variables libres también se pueden usar como parámetros virtuales y pasar a través de la función externa. Por favor, vea el ejemplo a continuación.

def outer(num1):
    def inner(num2):
        result = num1 + num2
        print('result:', result)

    return inner

f1 = outer(1)
f1(2)

输出:
result: 3

En este momento, la función externa tiene un parámetro virtual. Al generar una instancia de cierre, se debe pasar un parámetro a la función externa. Aquí, el valor del parámetro pasado es 1, que se almacena en num1 después de ser recibido por el función exterior. Cuando se ejecuta la función f1(2), es equivalente a llamar a la función interna interna, porque interna también tiene un parámetro, por lo que también se pasa un parámetro al llamar, y el valor del parámetro es 2, que se guarda después de recibirlo por la función interna en num2. Cuando se ejecuta la función interna, num1 se refiere al num1 de la función externa, su valor es 1, el valor de num2 es 2 y el resultado de la suma es 3. El resultado de salida final: 3.

2. decorador

1. Conceptos básicos del decorador

La esencia de un decorador es el uso de cierres, su función es agregar funciones sin cambiar la función original y el nombre de la función.

Principios: 1. No se puede modificar el código fuente de la función decorada 2. No se puede modificar el método de llamada de la función decorada.

Escenarios de aplicación del decorador:

  • registro de importación
  • Estadísticas de tiempo de ejecución de funciones
  • Preparación antes de ejecutar la función
  • Función de limpieza después de la ejecución de la función
  • Escenarios como la verificación de permisos
  • cache

Ejemplo: Tenemos la siguiente función Al agregar un decorador, podemos agregar una línea divisoria arriba y abajo del contenido de salida original.

Las funciones básicas son las siguientes:

def f():
    print('---test---')
f()

Utilizamos cierres para realizar esta tarea.

def wrapper(func):
    def inner():
        print("*****我是美丽分割线1*****")
        func()
        print("*****我是美丽分割线2*****")
    return inner
def f():
    print('---test---')
f = wrapper(f)
f()

Veamos cómo usar los decoradores:

def wrapper(func):
    print('正在装饰')
    def inner():
        print("*****我是美丽分割线1*****")
        func()
        print("*****我是美丽分割线2*****")
    return inner

@wrapper
def f():
    print('---test---')
f()

Veamos su proceso de ejecución:

(1) def wrapper(func), define la función decoradora y cárgala en la memoria.

(2) def f(), define la función. En este momento, se encuentra que hay @wrapper encima de él. @wrapper llama a la función decoradora. @wrapper pasa f como un parámetro a la envoltura. Esta oración es equivalente a f = envoltura (f).

(3) En este momento, wrapper(func) se ejecutará inmediatamente, generará "decorating", y luego internal se devolverá a f, en este momento f = inner()

(4) f() es una función de ejecución, e inner() se ejecuta realmente, por lo que generará "*****Soy una hermosa línea divisoria 1*****", func() es para f( ) Ontology Call, en este momento, generará '---test---', y finalmente generará "*****Soy una hermosa línea divisoria 2*****".

Encontramos que @wrapper es una simplificación de f=wrapper(f).

En el uso real, un decorador puede decorar múltiples funciones, solo agregue una oración sobre cada función decorada para llamar a la función decoradora.

import time

def timer(func):
    def deco():
        start_time = time.time()
        func()
        stop_time = time.time()
        print('the func run time is %s'%(stop_time-start_time))
    return deco

@timer      #@timer 相当于就是test1 = timer(test1);此时test1=deco,deco里面的func=最开始的test1
def test1():
    time.sleep(3)
    print('in the test1')
@timer      #@timer 相当于就是test2 = timer(test2);此时test2=deco,deco里面的func=最开始的test2
def test2():
    time.sleep(5)
    print('in the test2')

test1()
test2()

2. Múltiples decoradores

Se pueden aplicar varios decoradores a una función, el orden de decoración es de abajo hacia arriba y el orden de invocación es de arriba hacia abajo.

def wrapper_out1(func):
    print('wrapper_out1正在装饰')
    def inner1():
        print("这里是inner1-1")
        func()
        print("这里是inner1-2")
    return inner1

def wrapper_out2(func):
    print('wrapper_out2正在装饰')
    def inner2():
        print("这里是inner2-1")
        func()
        print("这里是inner2-2")
    return inner2
@wrapper_out1
@wrapper_out2
def test():
    print("--test--")

test()

# 输出
wrapper_out2正在装饰
wrapper_out1正在装饰
这里是inner1-1
这里是inner2-1
--test--
这里是inner2-2
这里是inner1-2

Proceso de implementación:

(1) Cargue wrapper_out1, wrapper_out2, pruebe en la memoria

(2) Primero decore la prueba, porque hay dos decoradores en la prueba, y la decoración sigue el principio de abajo hacia arriba, primero ejecute @wrapper_out2, que es equivalente a test = wrapper_out2(test), salida 'wrapper_out1 está decorando', y luego inner2 se pasa de nuevo a test; cuando se ejecuta @wrapper_out1, es equivalente a test = wrapper_out1(test), tenga en cuenta que la prueba entre paréntesis en realidad es inner2, la salida 'wrapper_out1 está siendo decorada', y luego se pasa inner1 volver a la prueba. Todo este proceso es equivalente a: test = wrapper_out1(wrapper_out2(test)), la prueba entre paréntesis es la prueba original.

(3) Al llamar a la función de prueba, ejecute interior1 primero, emita: "aquí está interior1-1", y luego llame a func(), donde func es en realidad interior2, así que introduzca interior2, emita "aquí está interior2-1", en inner2 La func es la función de prueba original, por lo que genera "--test--", y luego genera "aquí está internal2-2", después de ejecutar internal2, regresa a internal1 y genera "aquí está internal1-2".

3. Decora funciones con parámetros y valores de retorno

def func_dec(func):
    def wrapper(*args):
        print("average_value is %.2f."%(sum(args)/len(args)))
        result = func(*args)
        return result
    return wrapper

@func_dec
def add_sum(*args):
    return sum(args)

args = range(10)
result = add_sum(*args)
print(result)

# 输出
average_value is 4.50.
45

3. Ejercicios de funciones avanzadas

1. Práctica básica del decorador

Haga un decorador para agregar una cadena por encima y por debajo de la cadena de la función existente, el efecto es el siguiente:

def test():
    print("这是第一次装饰器编程练习")
test()

# 输出
Hello world
这是第一次装饰器编程练习
I love python

2. Usa el temporizador para decorar la función.

Cree un decorador para generar el tiempo de ejecución de cada función en función de las funciones existentes.

def test1():
    time.sleep(5)
    print("这是第一次装饰器编程练习")

def test2():
    time.sleep(8)
    print("这是第二次装饰器编程练习")
test()

3. Escribe un decorador con parámetros

def test(*args):
    print("这是第一次装饰器编程练习")
test('20010001','Tom')

# 输出
Id is 20010001,Name is Tom
这是第一次装饰器编程练习

4. Respuestas a los ejercicios del diccionario de la Lección 16

1. Ejercicios básicos de diccionario

dic = {
    'python': 95,
    'html/css': 99,
    'c': 100
}

'''
1.字典的长度是多少
2.请修改'html/css'这个key对应的value值为98
3.删除c这个key
4.增加一个key - value对,key值为php, value是90
5.获取所有的key值,存储在列表里
6.获取所有的value值,存储在列表里
7.判断javascript是否在字典中
8.获得字典里所有value的和
9.获取字典里最大的value
10.获取字典里最小的value
11.字典dic1 = {'php': 97}, 将dic1的数据更新到dic中
'''

print(len(dic))
dic['html/css'] = 98
del dic['c']
dic['php'] = 90
lst_key = list(dic.keys())
lst_value = list(dic.values())
print('javascript' in dic)
print(sum(dic.values()))
print(max(dic.values()))
print(min(dic.values()))
dic1 = {'php': 97}
dic.update(dic1)

2. Encuentra el valor b

Hay 17 pares clave-valor en el diccionario D, y cada par clave-valor se almacena en la forma de (a,b):v. Se requiere escribir un programa para encontrar el valor de b cuando las condiciones a= =10 y v==1 se cumplen.

D = {(4, 7): 0, (2, 6): 1, (10, 4): 1, (5, 11): 1, (4, 5): 1,
(2, 8): 0, (8, 11): 0, (10, 0): 1, (6, 11): 1, (9, 11): 1,
(1, 9): 0, (10, 1): 0, (7, 11): 1, (0, 9): 1, (3, 7): 1,
(10, 3): 1, (10, 2): 1}

for a,v in D.items():
    if a[0] == 10 and v == 1:
        print(a[1])

3. Estadísticas de frecuencia de palabras

# 任务1:将所有大写转换为小写
words = words.lower()

# 任务2:生成单词列表
words_list = words.split()

# 任务3:生成词频统计
words_dict = {}
for word in words_list:
    if word in words_dict.keys():
        words_dict[word] += 1
    else:
        words_dict[word] = 1

# 任务4:排序
words_dict_list = list(words_dict.items())
words_dict_list.sort(key = lambda x:x[1],reverse=True)
print(words_dict_list)
# 任务5:排除语法型词汇,代词、冠词、连词
exclude = ["the", "has", "of", "a", "for", "and","away","from","on","to","are","only","have","in","after"]

for i in range(len(words_dict_list)-1,-1,-1):
    k = words_dict_list[i][0]
    if k in exclude:
        words_dict_list.pop(i)

# 任务6:输出词频最大TOP20
print(words_dict_list[:20])

4. Busque el número de identificación

El número de identificación contiene mucha información, los dígitos 7 al 14 son información de cumpleaños, el penúltimo dígito es el código de género, números impares para hombres y números pares para mujeres. Es necesario generar los tres elementos en la lista de cuentas en el formato del diccionario de información.

account = ['Jone360403200105070312', 'Tom36040320020903043X', 'Susan360101200108110181']
info = {}
for x in account:
    name = x[:len(x) - 18]
    year = x[-12:-8]
    month = str(int(x[-8:-6]))
    day = str(int(x[-6:-4]))
    sex = lambda x:'女' if int(x[-2]) % 2 == 0 else '男'
    info[name] = dict(性别=sex(x), 生日='%s年%s月%s日' % (year, month, day))
print(info)

 

Supongo que te gusta

Origin blog.csdn.net/qq_40407729/article/details/111876295
Recomendado
Clasificación