Python - 函数 - 闭包、装饰器、递归

目录

一、闭包 - 内部函数包含,对外层作用域而非全局作用域的引用

1-1 闭包的意义 - 优先使用自己外层包裹的作用域

二、装饰器 - 闭包的一种应用方式

2-1 不使用装饰器实现装饰器的功能 - 使用闭包

2-2 无参装饰器(@装饰器名)

2-2-1 无参装饰器 - 书写格式

2-2-2 无参装饰器练习 - 认证功能装饰器

2-3 有参装饰器(@装饰器名(参数名))

2-3-1 有参装饰器书写格式

2-4 函数上叠加 多装饰器 的运行顺序

扫描二维码关注公众号,回复: 4184890 查看本文章

2-5 @wraps  对装饰器内层函数的使用 -- 让使用装饰器的函数 调用__name__,__doc__的时候可以指向原有函数属性

三、 递归  - 调用函数的过程中直接或者间接调用本身

3-1 二分法


一、闭包 - 内部函数包含,对外层作用域而非全局作用域的引用

def counter():
    n = 0

    def incr():
        nonlocal n
        # 修改嵌套作用域(enclosing 作用域,外层非全局作用域)中的变量
        x = n
        n += 1
        return x

    return incr


c = counter()
print(c()) # 第一次 n=0 x=0 n++
print(c())# 2 n=1 x=1 n++
print(c())
print(c.__closure__[0].cell_contents)  # 查看闭包的元素

1-1 闭包的意义 - 优先使用自己外层包裹的作用域

'''
闭包的意义:
返回的函数对象,不仅仅是一个函数对象,在该函数外还包裹了一层作用域,
这使得,该函数无论在何处调用,优先使用自己外层包裹的作用域
'''

# 应用领域:延迟计算(原来我们是传参,现在我们是包起来)
from urllib.request import urlopen


def index(url):
    def get():
        return urlopen(url).read()

    return get


baidu = index('http://www.baidu.com')
print(baidu().decode('utf-8'))

二、装饰器 - 闭包的一种应用方式

概念:装饰器他人的器具,本身可以是任意可调用对象,被装饰者也可以是任意可调用对象。
          即:装饰器是在不修改被装饰对象源码和调用方式的前提下,为被装饰对象添加新功能的工具

原则:

  1. 不修改被装饰对象的源代码
  2. 不修改被装饰对象的调用方式

目标: 在遵循原则1和2的前提下,为被装饰对象添加上新功能

运行原理:python解释器,一旦运行到@装饰器的名字,就会调用装饰器,然后将被装饰函数的内存地址,当做参数传给装饰器,最后将装饰器调用的结果赋值给原函数名

函数上添加多个装饰器的运行顺序:解释调用@语法的时候是自下而上运行(即从越靠近函数的执行器先先执行) 执行装饰器内部函数的wrapper的时候是自上而下

即,func = @outter_最外层(@outter_中间(@outter_内层))

2-1 不使用装饰器实现装饰器的功能 - 使用闭包

import time


# 原函数index(),无参
def index():
    print('welcome to index page')
    time.sleep(3)


# 原函数home(name) 有参
def home(name):
    print('welcome to index page')
    time.sleep(2)
    return 123


# 闭包函数,用来接收index的地址并添加新功能之后返回
def outter(func):
    def wrapper(*args, **kwargs):
        # 让传入的参数 原封不动,则可以解决原函数对参数(有参无参)的要求

        start = time.time()
        res = func(*args, **kwargs)
        # 运行形参函数,并且将func的返回值赋给res
        stop = time.time()

        print('run time is %s' % (stop - start))
        return res  # 返回传入函数的返回值

    return wrapper


index = outter(index)  # 将index地址传入putter,返回wrapper地址
# 当前重新复制的index保存着wrapper的地址
index()

home = outter(home)
# home=outter(最原始那个home函数的内地址) #home=wrapper函数的内地址
res = home('egon')  # res=wrapper函数的内地址('egon')
print(res)

2-2 无参装饰器(@装饰器名)

import time


def timmer(func):
    def wrapper(*args, **kwargs):
        # 让传入的参数 原封不动,则可以解决原函数对参数(有参无参)的要求

        start_time = time.time()
        # 开始时间
        res = func(*args, **kwargs)
        # 运行形参函数,并且将func的返回值赋给res
        stop_time = time.time()
        # 结束时间

        print('run time is %s' % (stop_time - start_time))
        print(res)
        return res  # 返回传入函数的返回值

    return wrapper


@timmer  # 调用timmer(foo)并赋值给foo,即foo = timmer(foo)
def foo():
    time.sleep(3)
    print('from foo')


# 在foo()函数上套上timmer的无参装饰器

foo()  # foo = timmer(foo)

2-2-1 无参装饰器 - 书写格式

'''
def outter(func):
    def wrapper(*args,**kwargs)

        … 逻辑代码块 …
        res = func(*args,**kwargs)
        … 逻辑代码块 …

        return res
    return wrapper

@outter
def index():
    pass

index()

'''

2-2-2 无参装饰器练习 - 认证功能装饰器

def login_out(func):
    def login_wrapper(*args, **kwargs):
        res = func(*args, **kwargs)
        return res

    return login_wrapper


def users_list():
    # 返回值为users_list
    users_list = []
    with open('user_and_pwd', 'rt', encoding='utf-8') as users_pwd_f:
        users_date = users_pwd_f.read()
        user_list = users_date.split('/')
        user_list.remove('')

        for i in range(len(user_list)):
            users_name = user_list[i].split('|')
            users_list.append(users_name)
    print(users_list)
    return (users_list)


def login_out(func):
    def login(*args, **kwargs):
        # 登陆判断 三次尝试机会 返回值: '登陆用户名'
        res = func(*args, **kwargs)
        count = 0
        uname_dic = {}
        for n in users_list():
            uname_dic[n[0]] = n[1]
        # print(uname_dic)

        while count < 3:
            name = input('请输入用户名:').strip()
            if name in uname_dic:
                pwd = input('请输入密码:').strip()
                if pwd == uname_dic[name]:
                    print('登陆成功!\n')
                    uname = name
                    login = True
                    break
            else:
                print('用户名不存在!')
                count += 1

        # return (name)
        print(res)
        return res  # 接受到原函数的返回值

    return login


@login_out
def index():
    print('认证功能装饰器')
    res = 123
    return res


index()

2-3 有参装饰器(@装饰器名(参数名))

def auth(driver='file'):
    def auth2(func):
        def wrapper(*args, **kwargs):

            name = input("user: ")
            pwd = input("pwd: ")
            if driver == 'file':
                if name == 'egon' and pwd == '123':
                    print('login successful')
                    res = func(*args, **kwargs)
                    return res
            elif driver == 'ldap':
                print('ldap')

        return wrapper

    return auth2


@auth(driver='file')  # 相等于@auth2 但在其内部可以使用dirver的值
def foo(name):
    print(name)


foo('egon')

2-3-1 有参装饰器书写格式

# 在无参装饰器的外层再包一层函数,最外层函数用来进行往内层传值
'''
有参装饰器(书写格式,做多三层)

def out(d='123',a=2,)
    def outter(func):
        def wrapper(*args,**kwargs)
            
            … 逻辑代码块 …

            res = func(*args,**kwargs)
                
            … 逻辑代码块 …

            return res
        return wrapper


@out(d='1111')
def index():
    pass

index()
'''

2-4 函数上叠加 多装饰器 的运行顺序

'''
解释调用@语法的时候是自下而上运行(即从越靠近函数的执行器先先执行) 
执行装饰器内部函数的wrapper的时候是自上而下 

即,func = @outter_最外层(@outter_中间(@outter_内层))
'''

import time


def outter1(func1):  # func1=wrapper2
    print('outter1')

    def wrapper1(*args, **kwargs):
        print('wrapper1')
        res1 = func1(*args, **kwargs)  # res1=wrapper2(*args,**kwargs)
        return res1

    return wrapper1


def outter2(func2):  # func2=最原始的那个index的内存地址
    print('outter2')

    def wrapper2(*args, **kwargs):
        print('wrapper2')
        res2 = func2(*args, **kwargs)
        return res2

    return wrapper2


@outter1  # index=outter1(wrapper2) #index=wrapper1
@outter2  # outter2(最原始的那个index的内存地址) ===> wrapper2
def index():
    print('welcome to index page')
    time.sleep(3)


index()  # wrapper1()

'''
outter2
outter1
wrapper1
wrapper2

'''

2-5 @wraps  对装饰器内层函数的使用 -- 让使用装饰器的函数 调用__name__,__doc__的时候可以指向原有函数属性

# ------ 不对内层函数使用 @ wraps() - ---
import time


def outter1(func1):
    # print('outter1')
    def wrapper1(*args, **kwargs):
        # print('wrapper1')
        res1 = func1(*args, **kwargs)  # res1=wrapper2(*args,**kwargs)
        return res1

    return wrapper1


@outter1
def index():
    '''
    index 功能
    '''
    print('index page')
    time.sleep(2)


print(index.__name__)  # 显示函数名字
print(index.__doc__)  # 显示函数的注释内容

# 使用装饰器之后 会显示 装饰器重新复制的函数对象数据


import time
from functools import wraps


# ------ 对内层函数使用 @ wraps() - ---
def outter1(func1):
  
    # 让使用装饰器的函数 调用__name__,__doc__的时候可以指向原有函数属性
    # 若有多层函数嵌套,只需要在最内层的装饰器添加 @wraps

    @wraps(func1)
    def wrapper1(*args, **kwargs):
        # print('wrapper1')
        res1 = func1(*args, **kwargs)  # res1=wrapper2(*args,**kwargs)
        return res1

    return wrapper1


@outter1
def index():
    '''
    index 功能
    '''
    print('index page')
    time.sleep(2)


print(index.__name__)  # 显示函数名字
print(index.__doc__)  # 显示函数的注释内容

三、 递归  - 调用函数的过程中直接或者间接调用本身

'''
函数的递归 : 函数嵌套调用的一种特殊形式,在调用函数的过程中直接或者间接调用本身

递归调用必须有两个明确的阶段:
  1.回溯:一次次递归调用下去,即一重复的过程
          但每次重复,问题的规模减少,直到逼近最终结果
          即回溯结果一定有一个明确的结束条件
  2.递推:一层一层的往回传值
'''

l = [1, [2, [3, [4, [5, [6, ]]]]]]


def s(l):
    for i in l:
        if type(i) is not list:
            print(i)
        else:
            s(i)


s(l)

3-1 二分法

nums = [13, 15, 17, 23, 31, 54, 87, 99, 104, 230]


def func(num, search):
    print(num)

    if len(num) == 0:
        print('can not find it')
        return
    middle = len(num) // 2
    if num[middle] > search:
        func(num[:middle], search)

    elif num[middle] < search:
        func(num[middle:], search)

    else:
        print('find it')
        return


func(nums, 3)

'''
[13, 15, 17, 23, 31, 54, 87, 99, 104, 230]
[13, 15, 17, 23, 31]
[13, 15]
[13]
[]
can not find it
'''



l = [1, 2, 10, 30, 33, 99, 101, 200, 301, 402]


def search(num, l, start=0, stop=len(l) - 1):
    if start <= stop:
        mid = start + (stop - start) // 2
        print('start:[%s] stop:[%s] mid:[%s] mid_val:[%s]' % (start, stop, mid, l[mid]))
        if num > l[mid]:
            start = mid + 1
        elif num < l[mid]:
            stop = mid - 1
        else:
            print('find it', mid)
            return
        search(num, l, start, stop)
    else:  # 如果stop > start则意味着列表实际上已经全部切完,即切为空
        print('not exists')
        return


search(301, l)

'''
start:[0] stop:[9] mid:[4] mid_val:[33]
start:[5] stop:[9] mid:[7] mid_val:[200]
start:[8] stop:[9] mid:[8] mid_val:[301]
find it 8
'''

猜你喜欢

转载自blog.csdn.net/qq_33961117/article/details/84308161