闭包&装饰器

闭包条件

  • 作用域 L_E_G_B
  • 函数中嵌套函数
  • 外层函数返回内存嵌套函数名
  • 嵌套函数有引用一个非全局据变量(E作用域)
def func():
    a = 100  # Enclosed作用于

    def inner():
        print(a)

    return inner


f = func()
print(f)  # 返回inner函数对象
f()  # 执行inner函数

作用:实现数据锁定,提高稳定性

装饰器

  • 知识储备:

    • 作用域 L_E_G_B
    • 高阶函数 :1.函数名可以作为参数传入;2.函数名还可以作为返回值
    • 闭包
  • 解释:

    • 装饰函数是一个闭包样式的函数,参数是被装饰函数
    • 被装饰函数会当作参数传入装饰函数中,然后返回给被装饰函数接收,所以我们执行被装饰函数的时候就等于执行装饰器函数的返回值--嵌套函数
  • 目的:遵循原则,开放扩展 关闭修改

  • 好处

    • 在不改变原函数功能的情况扩展功能
    • 执行原函数就能执行到扩展功能
  • 装饰器的应用场景

    • 登陆验证
    • 函数运行时间统计
    • 执行函数之前做准备工作
    • 执行函数后做清理功能
# 添加时间统计的装饰器
def add_time(func):
    # *args, **kwargs作用:有参数无参数的函数都能装饰 
    def inner(*args, **kwargs):
        before_time = time.time()
        func(*args, **kwargs)
        after_time = time.time()
        print(after_time - before_time)

    return inner


@add_time
def for_fun():  # for_fun=add_time(for_fun)   add_time(for_fun) == inner 
    for i in range(100):
        if i == 50:
            time.sleep(1)
            print(i)


# 所以执行for_fun的时候等于执行的inner
for_fun()
# 登陆验证的装饰器,每个页面验证登陆 如果登陆过不需要重复登陆
# txt数据
# {"name":"a","passwd":"1"}
# {"name":"b","passwd":"2"}
# {"name":"c","passwd":"3"}

cookie = "False"
def check_login(func):
    def inner(*args, **kwargs):
        global cookie
        if cookie == "False":
            print("身份验证失败请重新登陆")
            n = 0
            while n < 4:
                username = input("请输入你的用户名").strip()
                passwd = input("请输入你的密码").strip()
                # 读取用户信息文件,判断帐号密码
                with open("login_info.txt", "r", encoding="utf-8") as file:
                    for line_info in file:
                        user_info = eval(line_info.strip())
                        if username == user_info["name"] and passwd == user_info["passwd"]:
                            print("登陆成功")
                            cookie = "True"
                            func(*args, **kwargs)
                        
                    else:
                        print("用户名不存在或密码错误")
                        n += 1
            else:
                print("错误次数过多,退出登陆")
        else:
            func(*args, **kwargs)
           
    return inner


@check_login
def home():
    print("欢迎来到首页")


@check_login
def me():
    print("欢迎来到个人中心")


print(home())
print(me())

  • 装饰器装饰类
    • 将类对象传进装饰器,逻辑和装饰函数一样
    • 嵌套函数需要返回类实例对象
# 类装饰器,reture 实例对象即可
def Myfunc(cls):
    def inner(*args, **kwargs):
        print("进入装饰器")
        # cls是传进来的类 被执行实例化对象,初始化实例对象初始值
        # # 必须将对象返回给调用方接收实例
        return cls(*args, **kwargs)

    return inner


@Myfunc
class MyClass:  # MyClass=Myfunc(MyClass)
    def __init__(self, name, age):
        self.name = name
        self.age = age


# 实例化对象是执行装饰器内层函数后 返回的一个实例对象给m接收
m = MyClass("DaBai", 18)
print(m)  # <__main__.MyClass object at 0x000001E76D6A0358> 实例对象 
print(m.name)

类中常用的三个装饰器

  • @classmethod 标记为类方法,默认参数为"cls",类本身可以直接调用(类属性,类方法,类本身和实例都可以直接调用,实例属性和实例方法,类本身不能调用,所以将方法加上@classmethod 装饰器变成类方法后类本身就可以直接调用了)
  • @staticmethod 标记为静态方法,默认不需要传参,类和实例都能调用
  • @property 设置只读属性,被property装饰的方法可以像属性一样被实例调用,但是属性值为只读属性,不能被修改(毕竟原始还是个方法)
# 三个类装饰器
class MyTest(object):

    def __init__(self, name):
        self.name = name

    @classmethod  # 类方法
    def add(cls):  # 默认参数传cls==类本身,self==实例本身
        print("add方法")
        # print(cls)

    @staticmethod  # 静态方法 类本身和实例对象都能调用
    def static():  # 默认不需要传参数,不像类方法默认传cls,实例方法默认传self
        print("这个是静态方法")

    @property  # 作用:设置实例的只读属性,不允许被修改 ,不允许修改的属性这样设置
    def Name(self):
        print("被这个装饰的类方法,可以像是属性一样被调用")
        name = "pro"
        return name

    def run(self):
        print(self.name)  # 实例属性 name 可以修改
        print(self.Name)  # 被@property装饰的方法可以像属性一样调用,不可被修改


# 实例
t = MyTest("DaBai")

# 类方法(classmethod)
MyTest.add()  # 类直接调用
t.add()  # 实例也能调用

# 静态方法(staticmethod)
MyTest.static()  # 类直接调用
t.static()  # 实例也能调用

# 只读方法(property)实例方法可以像属性一样调用
# 实例属性允许被修改或者添加
print(t.name)
t.name = "Bai"
print(t.name)

# property  设置只读属性不允许被修修
# t.Name = "PRO"  # 报错:AttributeError: can't set attribute
print(t.Name)  # 被property装饰的方法可以像属性一样调用 但是属性值为只读属性,不能被修改(毕竟原始还是个方法)

t.run()

posted @ 2019-05-22 02:51  讲明白  阅读(185)  评论(0编辑  收藏  举报