闭包
def outer():name = '自学编程'def inner():print("Inner", name)return innerfunc = outer() # 返回的是inner的内存地址func() # inner()"""输出:Inner 自学编程"""
嵌套在outer函数内部的inner是无法调用到的,但是通过上述代码可以在外部调用到inner函数
闭包:outer函数中定义了变量name,按理说执行outer()之后,name变量会在内存释放,再去执行inner()程序会报错,因为无法访问到name,但是现在可以访问到name的值,这就是闭包,简单来说就是一个函数将内部函数返回,内部函数引用了上一级函数的变量,导致上一级函数的变量无法释放,这种现象就叫闭包。
装饰器
假如你是一个视频网站的后端开发,现在需要给日本专区和欧美专区添加认证功能
def home():print("首页")def japan():print("日本专区")def america():print("欧美专区")japan()america()
你现在开始加认证功能:
v1版本
account = {"is_authenticated":False,"username":"alex","password":"123"}def login():if account["is_authenticated"] is False:username = input("user:")password = input("password:")if username == account["username"] and password == account["password"]:print("welcome login...")account["is_authenticated"] = Trueelse:print("wrong username or password")else:print("用户已经登录")def home():print("首页")def japan():login()print("日本专区")def america():login()print("欧美专区")japan()america()
但这违反了开放-封闭原则,它规定了已经实现的功能不能再被修改,但是可以被扩展,即:
- 封闭:已经实现的功能代码不能被修改
- 开放:对现有功能的扩展
就像japan和america这两个函数功能已经实现,但是你在函数内部添加了login()也就修改了已经实现的源代码,但是你会不理解:不修改这两个函数怎么进行扩展呢?
其实是可以的,如下所示
v2版本
account = {"is_authenticated":False,"username":"alex","password":"123"}def login(func):if account["is_authenticated"] is False:username = input("user:")password = input("password:")if username == account["username"] and password == account["password"]:print("welcome login...")account["is_authenticated"] = Truefunc()else:print("wrong username or password")else:print("用户已经登录")func()def home():print("首页")def japan():#login()print("日本专区")def america():#login()print("欧美专区")login(japan)login(america)
这种方式在没有修改源代码的情况下对america和japan扩展了认证功能,但是你修改了调用方式!假如说你的这几个功能被100个人都调用了,但是你扩展认证功能之后,这100个人都要去修改调用方式,这肯定不行!
于是思考之后出现版本v3
v3版本
account = {"is_authenticated":False,"username":"alex","password":"123"}def login(func):def wrapper():if account["is_authenticated"] is False:username = input("user:")password = input("password:")if username == account["username"] and password == account["password"]:print("welcome login...")account["is_authenticated"] = True#print(func)func()else:print("wrong username or password")else:print("用户已经登录")#print(func)func()return wrapperdef home():print("首页")def japan():#login()print("日本专区")def america():#login()print("欧美专区")japan = login(japan) # wrapperamerica = login(america) # wrapperjapan() # wrapper()america() # wrapper()
因为闭包,wrapper()函数内还可以访问上级函数的变量,上级函数的变量并没有释放,这里的变量也就是func,所以说japan()调用的时候func此时引用的是japan函数,内部执行func()也就是调用japan函数,这就是装饰器!
这种方式既没有修改源代码,也没有修改调用方式。
简化写法
v4版本
account = {"is_authenticated":False,"username":"alex","password":"123"}def login(func):def wrapper():if account["is_authenticated"] is False:username = input("user:")password = input("password:")if username == account["username"] and password == account["password"]:print("welcome login...")account["is_authenticated"] = True#print(func)func()else:print("wrong username or password")else:print("用户已经登录")#print(func)func()return wrapperdef home():print("首页")@logindef japan():#login()print("日本专区")@logindef america():#login()print("欧美专区")japan()america()
被装饰函数有参数时
account = {"is_authenticated": False,"username": "alex","password": "123"}def login(func):def wrapper(arg):if account["is_authenticated"] is False:username = input("user:")password = input("password:")if username == account["username"] and password == account["password"]:print("welcome login...")account["is_authenticated"] = True# print(func)func(arg)else:print("wrong username or password")else:print("用户已经登录")# print(func)func(arg)return wrapperdef home():print("首页")@logindef japan(vip_level):if vip_level > 3:print("解锁本专区所有高级玩法")# login()else:print("日本专区")@logindef america():print("欧美专区")japan(4)america()
此时可以成功打印“解锁本专区所有高级玩法” 但是america没有传递参数会报错,如果传递了参数,可是america函数没有形式参数,还是会报错,这就出问题了,不能给每个专区写一个装饰器吧,其实有一种解决方案,呢就是非固定参数!
account = {"is_authenticated": False,"username": "alex","password": "123"}def login(func):def wrapper(*args,**kwargs):if account["is_authenticated"] is False:username = input("user:")password = input("password:")if username == account["username"] and password == account["password"]:print("welcome login...")account["is_authenticated"] = True# print(func)func(*args,**kwargs)else:print("wrong username or password")else:print("用户已经登录")# print(func)func(*args,**kwargs)return wrapperdef home():print("首页")@logindef japan(vip_level):if vip_level > 3:print("解锁本专区所有高级玩法")# login()else:print("日本专区")@logindef america():print("欧美专区")japan(4)america()
这样使用多个函数使用同一个装饰器,可以有传递参数的,也可以有不传递参数的
classmethod类方法
类方法不能访问实例变量,只能访问类变量
class Dog():name = "stupid dog"def __init__(self,name):self.name = name@classmethoddef eat(self):print(self) # <class '__main__.Dog'>print("dog %s is eating" % self.name)d = Dog("xiaoming")d.eat()
加了 @classmethod的方法是类方法,类方法的self不是实例,而是类本身,所以self.name访问的是类属性name,而不是实例属性name。
所以pycharm就直接把self改成了cls告诉你这不是实例而是类
class Dog():name = "stupid dog"def __init__(self,name):self.name = name@classmethoddef eat(cls):print(cls) # <class '__main__.Dog'>print("dog %s is eating" % cls.name)d = Dog("xiaoming")d.eat()
staticmethod静态方法
不能访问类变量,也不能访问实例变量
相当于把一个方法从这个类中脱离出来,变成了一个普通的方法
property属性方法
把一个方法变成一个静态变量,可以访问实例变量
class Dog():name = "stupid dog"def __init__(self,name):self.name = name@propertydef eat(self):print(self)print("Dog is %s" % self.name)d = Dog("xiaoming")print(d.eat)
反射
可以通过字符串的形式来操作对象的属性
