callable() # 可调用的(对象加括号可以执行特定的功能,如:类和函数)
可调用对象即将自身传入 callable 函数( callable(对象) )返回结果为 True 的对象
x = 1 print(callable(x)) # False # x() # 会报错,TypeError: 'int' object is not callable,说明 变量x 不是可调用对象 def func(): pass print(callable(func)) # True # 说明 函数 func 是可调用对象
闭包函数
闭:# 函数内部的函数(全局看不到)
包: # 内部函数引用了外部函数作用域的名字
一个简单的闭包函数案例(无参版)
x = 111 def outter(): x = 100 def inner(): print(x) # 找到的是局部的变量 x return inner # 没有调用,所以没有返回值(函数都没执行) x = 120 res = outter() res() # 100 # 并没有受到全局 x 变化的影响 ---> 上一篇博客命名空间的查找顺序知识点 def func(): x = 666 res() func() # 100 # 并没有受到func空间中 变量x 变化的影响
闭包函数的优点: # 无论在什么地方调用,目标函数(inner) 用的都是外部包围函数outter 中的变量值,不会受全局中变量变化的影响 ---> 可以方便调用函数,一次传参,多次使用
有参版案例
def outter(x, y): # x = 传过来的参数x, y = 传过来的参数y # x = 1 # y = 40 def my_max(): if x > y: return x return y return my_max res1 = outter(1, 40) # res就是my_max函数的内存地址 print(res1()) print(res1()) # 40 # 40 res2 = outter(90, 200) print(res2()) print(res2()) # 200 # 200 print(res1()) # 40
获取百度首页数据长度(模块知识先不用理解)
普通写法
import requests # 需先安装这个模块 (命令行输入:pip3 install requests,安装) # 第一个直接给函数传参 url1 = 'https://www.baidu.com' url2 = '...' def my_get(url): response = requests.get(url) if response.status_code == 200: print(len(response.text)) my_get(url1) my_get(url1) my_get('https://www.baidu.com') my_get('https://www.baidu.com') # 2443 # 2443 # 2443 # 2443
闭包传参写法(一次传参即可,避免多次传参)
import requests def outter(url): # url = 'https://www.jd.com' def my_get(): response = requests.get(url) if response.status_code == 200: print(len(response.text)) return my_get my_jd = outter('https://www.jd.com') # 一次传参 my_jd() # 无需传参即可使用 my_jd() my_baidu = outter('https://www.baidu.com') my_baidu() my_baidu() my_jd() # 92775 # 92775 # 2443 # 2443 # 92775
tips: 上面两种写法其实也对应的是给函数传参的两种形式 , # 直接给该函数传参 , # 通过闭包间接达到传参的效果
** 闭包传参就是给要接收参数的函数外层再套一层函数,给外层的函数传参,然后通过函数定义的命名空间查找顺序原理,让该函数获取到外层函数接收到的参数,间接传参,下文的有参数版装饰器用到的就是这个原理
装饰器
初衷(由来):想要在不改变函数源代码且不改变函数调用方式的情况下给函数添加新功能
开放封闭原则: # 对扩展开放,对修改封闭
首先要申明一点: 装饰器真的不难,真的不难,你只要跟着我理解一遍,以后就都不是问题了(忘了你就再看一遍嘛) ,装饰器只是闭包函数的一种扩展应用。
装饰器的推导过程
现有一个需求,给现有的shopping 功能扩展一下,自动判断其是否已经登录,未登录先登录(不改源码与调用方式)
def shopping(): print("我要开始购物啦!") pass
不让改源码又不让改调用方式,那咋整呢?(反正我想了半天是想不出来)
那就一起来头脑风暴一下
先抛开调用方式,我们可以在它调用前后加上自己的逻辑代码,然后封装成函数,通过调用这个函数实现添加功能的目的
def check_login(): if is_login: shopping() else: login() # 调用login 函数,登录,登陆结束后会接着执行shopping 函数 shopping()
如果尝试运用昨天的知识点,函数名可以被当做变量一样赋值传递呢?就是说我把 shopping = check_login ,那是不是说我再 shopping() 执行的就是 check_login() 方法了呢?
is_login = False def shopping(): print("我要开始购物啦!") pass def check_login(): global is_login if is_login: shopping() else: print("登录成功了") # 调用login 函数,登录,登陆结束后会接着执行shopping 函数 is_login = True # 这里写的是 = 赋值操作,局部命名空间会新创建一个变量is_login 而我们要用的是全局的,所以在上方要加上 global is_login shopping() shopping = check_login shopping()