闭包与装饰器

闭包

  • 见名思意,闭包就是关闭起来的包咯,之前我们都是将参数传递给函数,而现在是将参数包起来了,形成了类似于自由变量的概念,平时咱们在内部定义一个变量,在外部由于作用域的关系是调用不到的,而闭包是将变量包起来,不管在哪里都可以调用的到

    • 其实闭包利用了函数嵌套的概念,来看一组闭包的例子

      # 看例子之前呢,推荐一个交互式的python命令行,ipython,支持代码提示,行号,自动缩进等等的功能
      安装方式:
          win + r --> cmd --> pip install ipython
      ​
      # 咳咳 来看例子
      In [50]: def func1():
          ...:     x = 1
          ...:     def func2():
          ...:         return x
          ...:     return func2
          ...:

      # 这就是闭包了?是的
      闭包的特征:
        1 内部函数外部嵌套了函数
            2 外函数的返回值是内函数的引用
            3 内部函数引用了外函数的临时变量
             
      # 而且返回值变量是私有的
      In [51]: num1 = func1()
          
      In [52]: print(num1.__closure__[0].cell_contents)   # 查看闭包的元素
      1
      ​
      In [53]: num2 = func1()
      ​
      In [54]: print(num1()+1,num2()) # 修改了num1,也不会干扰num2
      2 1

装饰器

  • 闭包的另外一种表现形式,主要用于装饰功能,在不改变原代码以及原代码的调用方式,另外的添加额外的功能

    • 装饰器分为有参装饰器和无参装饰器两种

      • 无参装饰器

        # 以下代码在编辑器中实现
        import time # 时间模块
        def home():
            time.sleep(3)   # 睡眠3秒
            print('this is my home')
        home()
        ​
        this is my home
        home函数运行时间为3.003586
        # 上面是一个函数,现在为这个函数增加一个计算运行时间的功能
        import time
        ​
        def run_time(func): # func == home
            def wrapper():  # 真正作为装饰的函数
                start_time = time.time()    # 函数执行前增加的功能
                func()  # 执行函数(如果被装饰函数需要接收值,次数则需要写为 res = func())
                stop_time = time.time() # 函数执行后增加的功能
                run = stop_time - start_time
                print('%s函数运行时间为%f' % (func.__name__,run))
            return wrapper  # 返回装饰器,闭包的原理
        ​
        @run_time   # 是函数的语法(语法糖), 等同于 home = run_time(home)
        def home(): # 如果此处需要传参,在上边func也需要写入形参
            time.sleep(3)
            print('this is my home')
        ​
        home()  # 没有改变函数的调用方式
      • 有参装饰器

        # 装饰器需要传参该怎么办?来看
        # 实现传入一个int,传入几则函数多运行几秒
        import time
        ​
        def run_time(my_time):
            # my_time = 
            def wrapper(func):  
                def inner(name,second): # home
                    start_time = time.time()
                    res = func(name,second+my_time) # home
                    stop_time = time.time()
                    run = stop_time - start_time
                    print('%s函数运行时间为%f' % (func.__name__,run))
                    return res
                return inner
            return wrapper
        ​
        @run_time(2)
        def home(name,second):
            time.sleep(second)
            print('this is %s home' % name)
            return name
        ​
        print(home('dingh',3))
        ​
        this is dingh home
        home函数运行时间为5.000118
        dingh        
      • 装饰器代码实现认证功能

        #!/usr/bin/evn python
        # -*- coding: utf-8 -*-
        
        import random
        import pickle
        import os
        
        # 存放用户信息
        user_db = r'user.db'
        
        # 登录状态
        login_status = {}
        
        # 注册用户
        def register(db_file):
            count = 0
            while count < 3:
                # 注册用户密码
                reg_user = input('请输出注册用户: ').strip()
                # 输入空则继续输入
                if not reg_user:continue
        
                # 输入两次密码
                reg_passwd = input('请输入注册密码: ').strip()
                reg_passwd_repeat = input('请再次输入注册密码: ').strip()
        
                # 两次密码一致
                if reg_passwd == reg_passwd_repeat:
                    with open(user_db,'rb') as read_f:
                        # 加载用户信息
                        user_dic = pickle.load(read_f)
                        # 生成用户注册信息
                        user_dic[reg_user] = reg_passwd
        
                        # 存入用户注册信息
                        with open(user_db,'wb') as write_f:
                            pickle.dump(user_dic,write_f)
                            break
                # 两次密码不一致
                else:
                    print('两次密码不一致,请重新注册')
                    count += 1
                    continue
        
        # 认证装饰器
        def auth(play):
            def inner():
                # 文件不存在,则创建并写入空字典
                if not os.path.exists(user_db):
                    with open(user_db,'wb') as write_f:
                        user_dic = {}
                        pickle.dump(user_dic,write_f)
        
                count = 0
                while count < 3:
                    land_user = input('用户名: ').strip()
                    if not land_user:continue
                    # 用户已经登录则重新登录其他用户
                    if land_user in login_status:
                        print('您已经玩过了,下次再来玩吧')
                        continue
        
                    land_passwd = input('密码: ').strip()
        
                    with open(user_db,'rb') as read_f:
                        # 加载用户信息
                        user_dic = pickle.load(read_f)
                        # 如果用户存在
                        if land_user in user_dic:
                            if user_dic[land_user] == land_passwd:
                                print('登陆成功')
                                login_status[land_user] = 'success'
                                # 玩游戏
                                point = play()
                                # 返回用户及点数
                                return land_user,point
                            else:
                                print('密码错误')
                                count += 1
                                continue
                        else:
                            print('用户不存在,请注册')
                            register(user_db)
            return inner
        
        @auth    # 装饰函数  
        # 玩游戏
        def play():
            default_point = 0
            rule = input('按[1]可以查看规则')
            if rule == '1':
                print('''
                    规则: 玩家通过任意键进行摇色子,每次摇动为三个色子,结果是三个色子点数的和, 
                    谁摇的点数和加起来最大,谁就是赢家!!!
                    ''')
        
            def wave():
                # 修改外层函数内的变量
                nonlocal default_point
                count = 0
                shake = input('按任意键摇色子,每个玩家只有一次机会哦!')
                while count < 3:
                    wave_point = random.randint(1,6)
                    # 三个色子点数相加
                    default_point += wave_point
                    count += 1
                # 返回相加的点数
                return default_point
            return wave()
        
        
        while True:
            # 接收玩家的名称和点数
            game_player1,point1 = play()
            print('您摇的点数是[%s]\n%s' % (point1,'-'*20))
            game_player2,point2 = play()
            print('您摇的点数是[%s]\n%s' % (point2,'-'*20))
        
            print('游戏开始了'.center(30,'-'))
            print('本局游戏[%s]玩家,投色子点数是%d\n本局游戏[%s]玩家,投色子点数是%d' % \
                (game_player1,point1,game_player2,point2))
        
            if point1 > point2:
                print('[%s]玩家获胜' % game_player1)
            elif point1 == point2:
                print('双方平局')
            else:
                print('[%s]玩家获胜' % game_player2)
        
            inp = input('是否继续游戏?(yes/no)').strip()
            if not inp:break
            
            # 将输入转换为小写
            inp = inp.lower()
            if inp == 'yes':
                continue
            elif inp == 'no':
                break
            else:
                break
        色子小游戏

         

posted @ 2018-11-21 23:11  浩小白  Views(707)  Comments(0Edit  收藏  举报