闭包

  1. def outer():
  2. name = '自学编程'
  3. def inner():
  4. print("Inner", name)
  5. return inner
  6. func = outer() # 返回的是inner的内存地址
  7. func() # inner()
  8. """
  9. 输出:Inner 自学编程
  10. """

嵌套在outer函数内部的inner是无法调用到的,但是通过上述代码可以在外部调用到inner函数
闭包:outer函数中定义了变量name,按理说执行outer()之后,name变量会在内存释放,再去执行inner()程序会报错,因为无法访问到name,但是现在可以访问到name的值,这就是闭包,简单来说就是一个函数将内部函数返回,内部函数引用了上一级函数的变量,导致上一级函数的变量无法释放,这种现象就叫闭包。

装饰器

假如你是一个视频网站的后端开发,现在需要给日本专区和欧美专区添加认证功能

  1. def home():
  2. print("首页")
  3. def japan():
  4. print("日本专区")
  5. def america():
  6. print("欧美专区")
  7. japan()
  8. america()

你现在开始加认证功能:
v1版本

  1. account = {
  2. "is_authenticated":False,
  3. "username":"alex",
  4. "password":"123"
  5. }
  6. def login():
  7. if account["is_authenticated"] is False:
  8. username = input("user:")
  9. password = input("password:")
  10. if username == account["username"] and password == account["password"]:
  11. print("welcome login...")
  12. account["is_authenticated"] = True
  13. else:
  14. print("wrong username or password")
  15. else:
  16. print("用户已经登录")
  17. def home():
  18. print("首页")
  19. def japan():
  20. login()
  21. print("日本专区")
  22. def america():
  23. login()
  24. print("欧美专区")
  25. japan()
  26. america()

但这违反了开放-封闭原则,它规定了已经实现的功能不能再被修改,但是可以被扩展,即:

  • 封闭:已经实现的功能代码不能被修改
  • 开放:对现有功能的扩展

就像japan和america这两个函数功能已经实现,但是你在函数内部添加了login()也就修改了已经实现的源代码,但是你会不理解:不修改这两个函数怎么进行扩展呢?
其实是可以的,如下所示

v2版本

  1. account = {
  2. "is_authenticated":False,
  3. "username":"alex",
  4. "password":"123"
  5. }
  6. def login(func):
  7. if account["is_authenticated"] is False:
  8. username = input("user:")
  9. password = input("password:")
  10. if username == account["username"] and password == account["password"]:
  11. print("welcome login...")
  12. account["is_authenticated"] = True
  13. func()
  14. else:
  15. print("wrong username or password")
  16. else:
  17. print("用户已经登录")
  18. func()
  19. def home():
  20. print("首页")
  21. def japan():
  22. #login()
  23. print("日本专区")
  24. def america():
  25. #login()
  26. print("欧美专区")
  27. login(japan)
  28. login(america)

这种方式在没有修改源代码的情况下对america和japan扩展了认证功能,但是你修改了调用方式!假如说你的这几个功能被100个人都调用了,但是你扩展认证功能之后,这100个人都要去修改调用方式,这肯定不行!

于是思考之后出现版本v3
v3版本

  1. account = {
  2. "is_authenticated":False,
  3. "username":"alex",
  4. "password":"123"
  5. }
  6. def login(func):
  7. def wrapper():
  8. if account["is_authenticated"] is False:
  9. username = input("user:")
  10. password = input("password:")
  11. if username == account["username"] and password == account["password"]:
  12. print("welcome login...")
  13. account["is_authenticated"] = True
  14. #print(func)
  15. func()
  16. else:
  17. print("wrong username or password")
  18. else:
  19. print("用户已经登录")
  20. #print(func)
  21. func()
  22. return wrapper
  23. def home():
  24. print("首页")
  25. def japan():
  26. #login()
  27. print("日本专区")
  28. def america():
  29. #login()
  30. print("欧美专区")
  31. japan = login(japan) # wrapper
  32. america = login(america) # wrapper
  33. japan() # wrapper()
  34. america() # wrapper()

因为闭包,wrapper()函数内还可以访问上级函数的变量,上级函数的变量并没有释放,这里的变量也就是func,所以说japan()调用的时候func此时引用的是japan函数,内部执行func()也就是调用japan函数,这就是装饰器!
这种方式既没有修改源代码,也没有修改调用方式。

简化写法
v4版本

  1. account = {
  2. "is_authenticated":False,
  3. "username":"alex",
  4. "password":"123"
  5. }
  6. def login(func):
  7. def wrapper():
  8. if account["is_authenticated"] is False:
  9. username = input("user:")
  10. password = input("password:")
  11. if username == account["username"] and password == account["password"]:
  12. print("welcome login...")
  13. account["is_authenticated"] = True
  14. #print(func)
  15. func()
  16. else:
  17. print("wrong username or password")
  18. else:
  19. print("用户已经登录")
  20. #print(func)
  21. func()
  22. return wrapper
  23. def home():
  24. print("首页")
  25. @login
  26. def japan():
  27. #login()
  28. print("日本专区")
  29. @login
  30. def america():
  31. #login()
  32. print("欧美专区")
  33. japan()
  34. america()

被装饰函数有参数时

  1. account = {
  2. "is_authenticated": False,
  3. "username": "alex",
  4. "password": "123"
  5. }
  6. def login(func):
  7. def wrapper(arg):
  8. if account["is_authenticated"] is False:
  9. username = input("user:")
  10. password = input("password:")
  11. if username == account["username"] and password == account["password"]:
  12. print("welcome login...")
  13. account["is_authenticated"] = True
  14. # print(func)
  15. func(arg)
  16. else:
  17. print("wrong username or password")
  18. else:
  19. print("用户已经登录")
  20. # print(func)
  21. func(arg)
  22. return wrapper
  23. def home():
  24. print("首页")
  25. @login
  26. def japan(vip_level):
  27. if vip_level > 3:
  28. print("解锁本专区所有高级玩法")
  29. # login()
  30. else:
  31. print("日本专区")
  32. @login
  33. def america():
  34. print("欧美专区")
  35. japan(4)
  36. america()

此时可以成功打印“解锁本专区所有高级玩法” 但是america没有传递参数会报错,如果传递了参数,可是america函数没有形式参数,还是会报错,这就出问题了,不能给每个专区写一个装饰器吧,其实有一种解决方案,呢就是非固定参数!

  1. account = {
  2. "is_authenticated": False,
  3. "username": "alex",
  4. "password": "123"
  5. }
  6. def login(func):
  7. def wrapper(*args,**kwargs):
  8. if account["is_authenticated"] is False:
  9. username = input("user:")
  10. password = input("password:")
  11. if username == account["username"] and password == account["password"]:
  12. print("welcome login...")
  13. account["is_authenticated"] = True
  14. # print(func)
  15. func(*args,**kwargs)
  16. else:
  17. print("wrong username or password")
  18. else:
  19. print("用户已经登录")
  20. # print(func)
  21. func(*args,**kwargs)
  22. return wrapper
  23. def home():
  24. print("首页")
  25. @login
  26. def japan(vip_level):
  27. if vip_level > 3:
  28. print("解锁本专区所有高级玩法")
  29. # login()
  30. else:
  31. print("日本专区")
  32. @login
  33. def america():
  34. print("欧美专区")
  35. japan(4)
  36. america()

这样使用多个函数使用同一个装饰器,可以有传递参数的,也可以有不传递参数的

classmethod类方法

类方法不能访问实例变量,只能访问类变量

  1. class Dog():
  2. name = "stupid dog"
  3. def __init__(self,name):
  4. self.name = name
  5. @classmethod
  6. def eat(self):
  7. print(self) # <class '__main__.Dog'>
  8. print("dog %s is eating" % self.name)
  9. d = Dog("xiaoming")
  10. d.eat()

加了 @classmethod的方法是类方法,类方法的self不是实例,而是类本身,所以self.name访问的是类属性name,而不是实例属性name。
所以pycharm就直接把self改成了cls告诉你这不是实例而是类

  1. class Dog():
  2. name = "stupid dog"
  3. def __init__(self,name):
  4. self.name = name
  5. @classmethod
  6. def eat(cls):
  7. print(cls) # <class '__main__.Dog'>
  8. print("dog %s is eating" % cls.name)
  9. d = Dog("xiaoming")
  10. d.eat()

staticmethod静态方法

不能访问类变量,也不能访问实例变量
相当于把一个方法从这个类中脱离出来,变成了一个普通的方法

property属性方法

把一个方法变成一个静态变量,可以访问实例变量

  1. class Dog():
  2. name = "stupid dog"
  3. def __init__(self,name):
  4. self.name = name
  5. @property
  6. def eat(self):
  7. print(self)
  8. print("Dog is %s" % self.name)
  9. d = Dog("xiaoming")
  10. print(d.eat)

反射

可以通过字符串的形式来操作对象的属性