大家好,这节课呢是我们阿坝幸运法的第五节课啊, 主要呢是讲 for 这个运算符啊,那么 for 运算符他做什么用的呢 啊他主要是啊批量引入内表数据啊,就说我比如说我这个有个内表对吧?用放的关键字啊进行把这个内表这个做些循环呢处理,然后得到一定的嗯数据啊, 然后再返返回到一个变量里面来,这个变量也当然也是列列列表啊,哎呀啊例行的啊, 在列表负责语句中可以使用 for 语句从其他列表中批量引入数据并处理。 那么这个下面这个呃语句啊,我就那个啊, 先不看啊先不看,然后呢我们否语法先看否语法的样式是这样的否啊否的话就是说这个时候要先声明一个 呃结构类型的变量啊,或者是一个什么啊非洲生泡类型的一个变量啊,硬推爆啊,他原来像我们那个什么去下或者是点点的啊,或者是呃夹包里面啊,那个风一起的 这个样式啊,他会对这个内表啊,他不是印这个内表吗啊对这个内表进行循循环啊进行循环,当然他可以加后面加条件啊,就现在我们都现在我们在做路虎艾特的时候也可以路虎艾特后面也 可以加 wifi 条件对吧?只是对满足条件的呃肌肉做循环嘛,对吧,他可以是一样的。 那么第二种语法就是 for 加 with for with then and 呃这个 hunter 或者是啊 while 这个语法我们这个呃 我们大家看个 demo 程序看的更直观一点啊我们先看一下 demo 程序啊,看完 demo 程序之后我们再来看这个语句啊,就比较好理解,要不然有一点不好理解。 带帽语句啊,就是看带帽顺序啊,第一个呢,我们是讲先讲第一个那个样啊, for 的样式啊,他是这样的嘛,对吧?语法 这样的。然后呢我们讲啊,我们的 for 语句啊,这个 fort 的符,它是对列表进行操作的,是吧?所以这个时候呢,我们要先先定一个,呃例行啊,进例行的目的是定一个变量嘛,定个列表变量,呃列表嘛,对吧? 新买个内表啊,这个内表是新买完这个,我这里其实新买两个内表啊,一个是呃 这个啊 lt 杠 t 一个是这个啊,这个主要是用于否语句,否语句他处理完之后他要返回个列列表吗?对不对?有个编程在接收他啊,这个这个起这个作用啊,这个是用于就是我们 我们要对哪个内表做处理,对吧?我们先先声明个内表嘛,对吧?这个时候呢要并并把内表给他勾到一个值啊,勾到 那些初始词啊,那么这里呢,我们来看一下结果他的电脑类型,这个类型里面有什么?有靓号啊?物料类型,物料组,然后他文本是吧?文本啊, 好,然后呢?我通过这个我们上节课讲到的这个,呃 venerable 表达式是吧?来给他构造一个 纸啊啊,勾搭完之后我们那个表给把那个表给弹出来看一下啊, 对吧?那么这个是这个是 lt。 港地的这个表的数据啊,这是我们自己给他定的一个一个内表定完之后啊,好,接下来我们需要对这个内表进行操作啊,进行操作,然后 这个取消一下,取消注射一下 这个列表定好之后,是吧?然后呢?哎, 没取消的啊,并且开始加注册格式取消看注册。好,我们看那个新的语法啊,新的语法也是用 value 表达式啊。 value 表达式,不过啊 在这个括号里面啊,这个括号里面我们用用来放操作符啊,放,放。这个时候呢,我们要定一个什么?定一个变量啊,形成一个变量,来 来来,就像就他相当于什么呢?就相当于我们。呃 就相当于我们在做 loop, loop at 循环的时候,是不是比如说啊,我要 loop at 这个内表,对吧?循环处理码 upo 艾特这个 l t table 这个啊啊,这个变得,哎,并没有非常好看。复制。 呃。 in two 个什么? in two 一个变量,对吧? 好,就相当于这样啊,这然后呢?这个地方呢?就相当于相当于我们这个地方啊,说明呢,只不过是这里,这里要用对的声明。这里是 不需要的啊这是不需要的啊,它是它相当于一个一个结构啊,它相当于这个内表的一个结构啊。 in 这 for 这个结构。 in 这个内表啊,就相当于我就相当于什么呢? 就相当于我们在的正面循环一样的啊,每循环一次啊,对,这个立马进行循环嘛,每循环一次啊,他把这个什么把这个行的数据给引出到这个结构里面来,对吧? 这个是我们阿巴的,他也相当于什么?风一起啊,比如说我们在家吧,或者是这些下坡里面,是吧?有个叫风一起,是吧, 对吧?过一期里面,比如说,呃,挖一个对象,然后不接,是吧? 音啊,这个是比如说是 c 下边的语法啊,音什么音?应该什么?我们比如说我们一个而历史的,是吧?或者是一个历史的啊,就像这个语法一样,那么这个历史呢,就相当于我们这个列表啊,这个 obj 呢,就相当于我们这个什么,这说明这个啊,只不过我们这里是不需要加什么前面声明的。呃, 关键字啊,不需要用 deta 啊,或者是什么啊,或者声炮,这不需要的啊。然后呢,其实就是这样循环处理啊,每每循环一次,他把这个数据可以什么音拖到这个表这个结构里面来,是这个意思啊, 就循环这个 lgd, 它并并将行项目啊复制给这个这个变量啊, 然后这里加个 index 啊, index in to 这里的话 index 就是。呃航行接触器啊,加航行接触器,就 说当前的函号,把当前的函号给哎写入到这个变量上,在这也是临时声明一个变量啊,这是这也是临时声明一个变量。然后呢?嗯, 可以后面加 vr 条件啊, vr 什么 vr? 这个 word 列型等于,呃,等于 four 的,按的这个 word 组等于九的啊,这是负责条件啊。 然后呢? well, 最后返最终哈,最终我们讲了我们这个是对一个列表进行或循环进进行做 循环处理,对吧?那最终会返回一个内表,是吧?那返回内表呢?呃,返回什么字段呢?这里啊,这里啊,这里外外外完之后就在 发个什么字段,就这里啊,本来字段这些字段,呃,这些字段啊,这些字段是来自于这个内表里面字段啊,这个,比如说我这个,比如说我这里有个特特色的二行不行?不行。为什么?因为我这个内表里面他没有这个特色的二,对吧? 好,呃。看一下啊,然后 然后我们就可以运行一下啊,运行一下,运行一下之后可以把这个运行的结果返回来弹出来看一下,可以去掉, 你先下。 ok, 没问题啊。啊,这个是这个是。呃。原表对吧?记得表的,记得表数据啊,然后这个是构建一个表表构建好之后啊,这是否语句啊?否语句啊, 玩一局。然后呢?这里的只不过是这里的什么?呃物料组把改了啊。物料组改成什么?物料组改成他对应的 喊号了啊,就是缩影号。因为什么呢?我们这里是物料组等于什么?等于这个变量。这个变量又等于什么?这个变量是在这里生命的啊,这个变量他他就等于什么?等于这个等待开始就缩影啊,就是缩影号啊, 这几个行号我们可以看一下。呃这个物料组等于是吧?负五的,负五的有,有这三行是不是?并且呢?好,这五角形等于负的,然后呢?这个,呃 过来组等于九的,是吧?啊?只有这条满足哎。只有这两行满是满足条件的,对不对?所以我们这个二七杠 for 这个列表是吧?他的值就是就是这两就是这两行,对吧? 好,然后然后我们再试一下啊,我们改个别名试一下可不可以啊? 不行,因为什么?它提示这个 text 三不存在,对不对? text 三,为什么?因为它是循环这个表,对不对?它是循环这个表,而这个表里面没有这个子弹,对吧?所以是所以它报错啊。 好,那看完这个 demo 之后啊,我们再回头看一下我们这个文字说明啊, 使用放语句时啊,需要为内表定义临时工作区,对吧?看我们这里是不是是不是有定一个临时工作区啊,对吧?啊 啊,仅允许在当前语句中使用,那么这个临时工作曲这临时这个变量,对吧?临时变量是吧?包括这个临时变量呢?他仅限于我这个语句当前这个语句的使用啊,他不是不是个局部变量啊,他比局部变量还要小,他只剩 在这个语句中来使用它啊, 他生命周期很短对吧?我这个鱼执行完之后对吧,他就没了,是吧。 然后呢,复制过程中会使用到该工作区啊。 单在 vr 条件里啊,只能直接使用列表的自断米啊。在 vr 条件里只能使直接使用列表的啊自断米啊,这里啊里边的自断米。 需要注意的是外号后面记得条件语句必须使用小括号包起来,对吧?看我们外号这个什么外号外号这个这个语句是是外号语句嘛,对吧?他必须要使用小括号包起来。 index into 电影的临时变量可用来记录当前操作行的训练啊,你可以也可以叫说叫行号,也可以叫什么。呃缩影号啊,缩影啊,它的作用是与路虎语句中的呃, 系统编的 sy 杠 table 给 next 内饰啊,就跟这个这个边上就是内饰啊。好,接下来我们再看这个语句啊,这个语句啊,它的 demara 程序啊 啊这个代码题就就这个啊,我们把这个给取消注视, 把这个给他注视掉,他检查一下。好,我们看一下啊, 这里的首先定一个内形啊,一个结构内形啊,然后再定一个表啊表内形啊。再定一个表内形, 然后这里定了个定了个变量对吧?定了个变量然后它等于什么 value value 这个表面形的啊然后后面里面加 for 啊这个 for 呢?呃 这个时候呢他其实是返回一个列表啊用了用了 for 之后他就返回一个列表,但这个时候他其实是,呃不是对列表就操作了啊。这个时候你看啊 这个时候它有点像我们加班里面或者四点在上面里面就啊 for 啊有点像这个啊。比如说我 int 一个 int 一个变量好吧。哎好吧。 int 个变量等于零,嗯哎 再小于。比如说我让它循环二十次啊然后每循环 是白酒至尊一啊白佳佳对吧?他有点像这个意思啊他现在他的效果就等同于这个啊等同于这个。 然后呢或循环之后他每次是吧每每循环一次是吧。他这里是什么我们我们这里是 for 啊 for 一个接啊接等于十一那我看这里这里变成接也可以啊啊接啊。 这这这是 c 加法或者简单啊或者是加班的语法啊。对比一下。嗯 啊他是从 c 开始循环是吧那么我这里可以改成 c 啊那么 他他每循环一次是吧他接就加十是吧我这个是至尊一是吧。呃。加十的话。呃 姐佳佳我们这里好像不能不能这个不能这样,但是我可以可以在什么可以在这里面啊。他每循环一次我让他接 加十四个意思啊可以可以可以像这样啊这是 java 语法啊。哎然后呢直到直到他。什么大于四十啊直到他大于四十 这个时候呢?直到他大于四十他就不凶他就他就他就,就是说当这个接什么他没循环时,他要加十,是吧?当他只变成什么大于四之, 它就不再值钱了。我这个我这我这也一样啊,对吧?我这意思说啊,他不大于四十就值钱,大于四十就不值钱了,就循环就就就退出了,对吧?就这个意思啊。好,然后 然后他每循环一次呢?每循环一次,嗯,他这个 这个字段就等于接这个字段就等于什么?等于接加一啊? 三个钻等于接加二,然后呢他会自动会返回一个内表出来,对吧?我们可以把这个内表给弹出来啊,弹出来运行一下试一下看一下 啊。这个是那第一个带帽的表啊,然后这个就是我们这个表,看具体看 it 宝啊,我们可以把它给截图看一下啊。哎,截图截图,这个截截不了图是吧?截不了图的话 这个头发拷贝一下的啊那我就把拷贝一下啊,拷贝一下拷贝一下。我看放在哪里哪个位置啊? 放在放在这个位置试一下 啊,貌似可以啊。啊?那么那么他就这样的啊,他反过来三三列吗?对吧?就是 十一十二,十三,二一二二二三。我们看一下啊,我们对比一下,然后把它关掉对比一下,然后把这个给缩小一下 啊,我们可以看一下。我们这个是。呃。从十一开始,是吧?第一次循环对吧?他就等于什么?他就等于十一,对吧?他就等于十二,他等于十三,第二次循环,是吧?他就变成什么了? 因为他是之前是。呃。之前是十一吗?是吧?第二次循环加十就变成二十一了,对吧?就变成二十一了,对吧?他变成二十一,第二列就变成什么二十二,第三 就变成二十三嘛?啊?第三次循环的时候,第三次循环的时候啊, 他就他就是三十一嘛,对吧?他没因为他每次每次去都都加加,加什么加十嘛?他他变变成那个第三次的时候,他变三十一,然后他就这个第二列三十二,他是三十三,对吧?他三十三,然后他三十三的时候啊? gs 三十一吗? gs 三十一,嗯,三十六,再加十就变成四十一了,是吧?到四十一了之后他就大于四十,就就什么就循环,就解除了,就不再执行了,就不再循环了。这个意思啊,所以他就能返回,返回个内表,他内边的值啊,内边的值就就这样了。 好,那么这两个带脉程序呢?啊,都演示完了啊,这个暴雨啊,说实话应该应该是感觉啊,其实作用不是很大啊,只是说像作用不是很大。
粉丝180获赞5872

下面讲一下购物原理,函数的创建和调用好,比方说我们这里面要定义一个函数,通过 funk 这样一个关键词,其实我们之前经常用的 funk me 啊,它本身就是一个函数, 总是以 one 作为一个前缀。说明啊,函数名可以随便起好括号,括号里面是你传给这个函数的参数啊,可以没有为空,也可以有多个。这里面我们传两个参数,参数和 b 参数, 那么 a, b 它们的类型需要指定一下,比方说都是整数。 好,后面呢,是说你这个函数返回的函数是什么类型?比方说我们要求 a 加 b 的 和,我们要返回一个整型 int, 大 括号是函数体,函数里面具体怎么执行? b 冒号等于就是声明并赋值,冒号等于 a 加 b 求和结果之后呢,通过 return 啊, return 关键词 来返回,相当于说你把这个 c 最后放到了这里面啊,这是一个简单的函数定义,对吧? 有入仓,有出仓,既然有出仓,那么就需要有一个 return 好, 然后我们去调用,我们在命里面呢,去调用刚刚定义好的这个函数, 我们随便定义两个变量, m 和 n, 它们等于四和七。然后呢,我们去调用 for 这个函数,说 q 它等于哎,开始调 for 这个函数啊,把 m 和 n 给它传进去, 我们一般说这个 m, n 呢,它们是实参,而 a, b 呢,是形参。实际上就说我把这个 m 传给了 a, 把 n 呢传给了 b, 那 最后它们是合,应该是十一对吧?那十一是相负给了 q, 我 们可以把 q 打出来看一看 啊, q 打出来它应该是十一对吧?我们把代码乱起来试一下。 好,它返回的是一个十一。好,我们再来仔细看看这个函数啊,那么 a, b 它们都是整型,那实际上呢,你可以把这个 int 省略不写 啊,可以简单的这样写,因为它们内心相同嘛,可以合并一下,这是一种写法。另外呢,有些函数你会发现它可能没有返回值 啊,没有返回值,那这个时候人家可能比方说就仅仅是简简单单的进行了一个打印, 哎,它对 c 呢进行了一个打印,然后我在内函数里面去掉负这个函数的话,因为它没有返回值嘛,所以你不能给它再赋给任何变量,把这部分给删掉。 好,没有返回值,你就直接这样去掉这个函数就可以了啊,它不需要赋给任何变量。好,我们可以把代码呢运行一下, 它从命里面作为程序的入口,进来之后呢,去掉到这个负,而负,在第九行进行了一次打印,输出。十一。 好,我们再展示一个一函数,它甚至可以有多个返回值啊,比方说我有两个返回值,都是 int, int, int, 当你有多个返回值的时候呢,需要使用括号把它们给括下来, 比方说 c 是 和那 d 呢?它等于 a 除以 b, 一个是求和,一个是求商和。我返回 c 和 d, 返回 c 和 d, 因为你要返回两个值嘛,那这个地方需要加括号,回通的时候,这个地方不需要加括号。好,我去掉这个函数的话,那我自然需要两个变量来进行承接 p 和 q, 调这个函数 m 和 n 返回两个值,然后把 p, q 进行一个打印 好,转一下, 这里面呢, p 是 它们的和四加 t 等于十一 q 呢,四除以七,它们都是整数嘛?整数相处的话,结果还是整数,所以说本来是零点几,然后一旦取整就只剩下零了。 这个地方可不是四舍五入啊,就说它把小数位全部抛弃,就只剩下零了,这个地方是零。 ok, 那 我们再来展示一个。嗯,构源里面的一个设计哲学叫做异常就地处理,我们会经常看到构标准库提供了的函数呢,它会给你返回一个 l 和 l。 往这写一下啊, arrow 是 这个啊,这种类型。 arrow 是 个语言里面很特有的一种数据类型,它跟它语言里面的异常是一个概念, 只不过呢,构源里面它更强调异常要就地处理,那其他语言通过串开写补回上之后,它可能不处理啊,继续不停地往外层抛,不停地 raise 或者说 throw, 对 吧?那么构源强调是就地处理好,那比方说这里面我 先判断一下,本来是想计算它们俩的这个商嘛,那我可能需要先判断一下啊,这个 b 它是否等于零? 好,如果等于零的话,那就显然不能让它一个数去除以零嘛,它会发生处理异常。所以呢,我先判断,如果说它等于零的话,那么我就 return 值,就 ten return 一个负一零吧,零做一个特殊标志吧, 其实这点是零或负一无所谓,重要的是我后面这个 arrow 啊,我要返回一个 arrow, 我 如何去构造到一个 arrow 变量呢?哎,通过 arrows 点 new, 这我说这个除数为零。好,那这个 arrows 呢,是需要你引入的啊,它是够标准库里面自带的一个包 arrows。 好, 这样的话我在第十行整个 return 行,整个函数就返回了,后面的根本没有机会执行,如果说 b 不 等于零的话,才有机会执行到第十二行。 好,那这个时候的话我要返回什么呢?一个整数为一个 arrow, 整数就是这个 d, 它们的商 io 呢?这个时候没有任何异常发生,所以就返回一个 new 就 可以了啊, new 也是呃,个语言里面所特有的一种数据类型,它跟它语言里面的 no 比较,呃,等同啊, 是没有发生异常嘛, new 就 可以了。好,那我在调用这个复函数的时候呢,就需要注意一下 这个地方,我们一般用 arrow 表示一个错误,那我们一定要先判断一下啊,就是这个屁能不能用,关键取决于这个 arrow 它是否为六,有没有错误发生。 如果说它等于六,说明没有异常发生,那我可以放心的去用这个屁 啊,那我就可以说这个商为 p 啊,如果说发生异常的话,那这个 p 就 不能用了啊,你就应该把异常呢给比方说打印出来, 发生了异常 error, 好, 我们来运行一下。 好,他说这个商为零,因为这个期它不是零嘛,所以没有想发生,我们把这个期改为零,故意让它发生一次一场, 他说发一场除数为零,对吧?那其实我们更常见的写法是什么呢?是在这个地方把一副语句直接提前到第十八行来写, 直接这样写,哎,直接这样写。好,我们说 if 表达式后面呢,实际上是用分号隔成两部分,第二部分才是你要判断的条件,而第一部分呢,实际上你可以进行一些局部变量的抽象 啊,跟刚才写法完全等价,只不过呢,会看上去更加简洁, ok, 结果一模一样。

如何将一个 excel 模板把它转换成 transformation 的代码?首先你把这个模板做好之后呢,在它值的位置输入几个容易识别的数据,是吧?我们输完了之后呢,点文件另重围, 找到这个另存的位置,然后在这个保存类型选择 charmair 电子表格,或者叫 charmair 电子表格二零零三,然后点保存,点试 保存完了之后呢,找到这个保存的文件生成的文件,点右键打开方式记事本打开之后呢,把上面这两行删掉, 然后再找到那个刚才那个值的位置啊,那个值就是 f 一, f 二、 f 三这几个位置,是吧?把这些位置呢?把它替换成替换成这段这段代码啊, 这个就是相当于把这个呃固定的值把它换成了这个变量。 好,替换完了这个之后呢,在这上面有两行,在这个肉开始肉结束的这个中间的位置加入 另一档代码。插入哪一档代码呢?这一档啊,插入这档代码, 然后在这在这个值的最下面结束的位置,也有一个任务结束的这个位置,把它呢加入另外一个,插入另一段代码,就是这段代码。 好,这个插完了之后呢,我们再 找到这个,找到这档代码,在那个插门文件里面找到这档代码, 找到这档代码,把它删掉啊,看到没?把这档删掉, 删掉完了之后呢,你再把这个所有的这个代码全选, 然后复制,把它呢 copy 到我们 transformation 的那个那个位置,比如说这个 transformation 啊,放到最后两行的前面这个位置, 然后激活就可以了。

hi, 零基础开始学第二章四点二,函数嵌套的定义与调用的区别。好,那函数里面定义函数啊,其实就像我们的循环里面定义循环一样,那函数定义函数呢?我们把它叫做函数的嵌套, 那这边函数嵌套的时候呢?我们啊有定义,那这边咱们就有什么函数的调用,是吧?那咱们这边呢,就来看看这个函数嵌套里面怎么来去定义,怎么来去进行调用。 好,那首先的话呢,我们在学习这个函数嵌套定义的时候呢,咱们得学另一个概念,那这个概念的话呢,其实会影响我们的函数里边定义的函数,那这个叫什么?叫做变量的作用域, 那我们再去定义这个函数的时候,大家有没有发现函数里边的这个变量在函数外边呢?我们访问不到,那在这里呢,咱们可以看一个例子啊, 好比如说这个例子啊,这个例子的话呢,我们先把这个外边的输出咱们给它注,咱们看,我在函数里边定义了一个叫做 number 的 这个变量,那咱们去 print 输出 啊,你看这是没有问题的,是吧?好,那这个函数里边定义的变量,如果在外边进行输出,你看会有一个什么样的问题啊? 好,执行了之后啊,我们会发现什么啊?函数外边的这一行,第六行他显示什么? name, arrow, 是 吧?那我们的这个 number 这个变量是没有被定义的,可是你看这个程序明明已经被定义啊, 那这个其实就会涉及到一个什么问题啊?叫做变量的作用域的这个问题,那这个作用域的时候呢?我们通过这个观察其实可以得到,在我们的函数内部定义的变量,外部我们没有办法访问到。哦,那这个其实也是什么?也是我们的 return 存在的一个意义,是吧? 要通过 return 才能返回到我们的函数的外部去,那函数如果是在外部定义的变量里边,我们说能不能看到呢?哎,咱把它拿到外面去, 我调整一下格式。 好,这函数里边我们给它定一个 pass 啊。好,咱们看函数外边定义的变量在外面能不能读取到呢?哎,是可以的。好,函数外边定义的变量在函数里边能不能读取到呢? 好,咱们看也是可以的。好,那这时候大家其实就想到了一个更加诡异的这个问题了,那啥问题呢?那在里边我们说函数外边定义,里边也定义,那这又是什么结果呢?你看还是可以读取到。那这时候呢,我们其实就想了, 既然里边外边都定义了,我故意把里边的值给他改一个值。好,外边是一百,里边是二百,那这里边能不能读取到呢?好,咱们看也能读取到,而且呢里边读取的什么?是内部的这个 number 啊,那外边读取的呢?是外 number 啊,那这边其实就说明什么?虽然他们是相同的名字,但是呢,他写在了这个函数内部和函数外部,那其实他就表示什么?两个不同的这个对象 啊,那这边就相当于什么?我们经常见到两个重名的人,但是是两个实际的一个个体,是吧?啊?为了我们去区分他怎么办呢?我们这一个把它装在函数的外边,一个把它装在函数的里边。 好,那为了研究我们的这个同名的变量,在函数内外这样的一些情况,我们就把它叫做什么呢?叫做一个函数的里边的变量的作用域的问题。 好,那这边我们回来啊。好,回来,那这边我们去研究的时候呢,咱们第一个要研究的问题叫做变量的作用域, 那 python 怎么来去规定我们的这个变量的这个作用域的呢?他有一个叫做 l、 e、 g、 b 这样的一个规则。好,这 l 是 指什么呢?叫做本地变量。好,啥是本地变量啊?我们这函数内部的这个其实就是本地变量 啊。那这个本地变量的话呢?是什么呢?是我们最优先能够操作到变量,然后这个最优先是啥意思呢?我还得给你切回这个代码啊。你看一下,我们在外侧有一个 number, 内侧也有一个 number, 那这个时候呢?我们读取到的这个 number 优先读谁啊?读我们的这个本地的,是吧?比如说如果你的函数内部有变量,那我们这边这输出的这个变量值优先读取内部的。哎,你看,所以内部我们读取的是二百,是吧? 好,那如果呢?我们的内部没有定义这个变量执行的时候,他就在读什么呢?在读取我们的外部的。好,那这边我们在读取的时候,那除了 local 我 们就读取外部,外部的这个变量称作什么呢?称作 global 啊,大局变量。 好,那除了这个内部和外部的这个全新变量之外的话呢?我们其实还有俩,还有一个叫什么?叫做 b 包变量,那咱们就是主要要学的这个 b 包变量,那 beauty 叫内置变量。这个内置变量是什么?是我们通过音 part 来去导入的 这些库,还有什么?还有我们的这个 python 在 运行解释器的时候就自己带的这变量,这个就是内置变量,那么读取的时候他的顺序是什么呢?先从本地的,哎,然后呢?就是一会我们要讲的 b 包, 哎,然后再没有再去找什么呢?再去找全局的,再没有了,再去找这个 beauty 啊,内置,所以呢,它有一个叫 l e g b 规则,就是我定义变量,我要优先这样去找, 那如果找不到的话,我们就会什么啊?就会给你报错了,那比如说这里边我们在内部先找到内置的这个 number 啊,找得到。好,那这边内部输出。 好,那这边如果呢?我们的这个程序外部没有定义好,那这边我们再去执行的时候,这个作用域啊,他仅限于我们的内部。好,那这边我们在外部,他已经在外部了,那他去找 找外部的 number, 那 外部的 number 没有,那我们这边在执行的时候自然就会出现一些报错啊,等等这些情况啊。我这个呢,因为是之前已经直过一次啊,所以在这边他没有报错,那这边你去把它这个重启内核来,这意思就是让他重新再来运行一次,那这时候呢,就 外部的变量了,是吧?执行的时候就会报错了。好,所以在里边的话,大家一定要注意啊,我们再去用这个函数的时候,函数的这个变量啊,写在里边,写在外边是有区别的,这是什么呢?它的作用域的这个问题, 那对于这个作用域的话呢,那限阶段要让大家去知道写在函数外的,写在函数内的啊,那这边如果有重名的时候,不同值的时候,那我们这边在输出的时候是什么样的一些结果? 好,那这个我们了解了变量作用域之后,接下来我们就要看了变量的作用域这里边有个叫 b 包变量,那啥是 b 包变量呢啊?咱们来看看。 b 包变量的话,就是指我们的函数内部再次定义的一个内部的这个函数,哎,他就形成一个叫做 b 包这样的一个概。那 b 包作用域之内呢?内部函数可以访问外部函数的这个变量,哎,啥意思啊?就是指我们定义的这个变量, 哎,咱们这叫做 alt 是 吧?好,这是外部的,然后在这里边呢,变量啊,函数内部啊,我再去定义一个函数,哎,内部的函数是吧?好,那在外部函数和内部函数当中,我是不是也可以去定义一个变量? 好,那这里边我们这个变量对于我们的这个内部的函数来说,其实它就叫做什么呢?就叫做 b 包变量,就是我们在最开始的 l e, g b 这里面提到的啊, n clothes 的 啊,就是这个变量啊。 好,那我们看这个 b 包边还有什么样的一个好处呢?那只有我们在函数内部再次定义一个函数,就形成了一个 b 包。好, b 包的话呢,那我们内部函数是可以访问外部函数的这个变量的,那这样的话呢,我们其实就可以形成一个什么外部函数里面再去定一个内部的函数, 哎,可以形成这样的一个做法。好,形成这样的这个做法后啊,我们说了,咱们去调用的时候就可以非常方便的用内部的函数去引用外部函数这些东西了。那比如说我在里面去这样去定义的。 好,定义的时候我们定一个外部的函数,那外部函数我们有一个变量,一的话,我们内部函数再去定义的时候,我们其实就可以使用我们的这个变量。 哎,我们可以使用我们的这个变量,那这边如果使用的时候,我还希望把这个变量再从外部再传出去,可不可以呢?哎,也是可以的,那咱们可以就像这样的一个方式来去进行定义。好,那在这里面呢,我们来去通过代码来去查看一下。 好,在这里我同样是两个啊函数啊,一个外部一个内部,好像这里边写成 alt 和 e 呢,大家可能会看的更清楚一些,是吧?好,那这样把它定义成 alt。 好,中间这部分我先给它注视掉。啊,咱们来看看。好,定义成 alt 之后,那这边我们去执行这个 alt 的 这个函数,我运行它,是吧? 好,统一改成 alt。 啊,好,那这边我们去定义这个函数,那这个函数我就可以去什么呢?去调用它了,对吧?好,调用之后我们是不是可以把函数的执行结果返回给我们的这个另外的一个变量啊? 好,那返回的时候我们说返回什么样才能调到我们的这个内部的这个函数呢?好,我返回的时候是内部函数的一个对象, 返回的时候是内部函数的对象,我就没有去调用它啊,返回的是这个,返回的是内部函数的对象。好,我把它叫做 e, 这样大家可以看一下。好,调用外部函数,然后呢,这边我们返回的时候是内部函数的对象。好,那这边这 f 其实拿到的是谁啊?是不是就这个内部函数的这个对象啊?咱们可以看一下。 好,内部函数的这个对象的话呢,那这边我们再去使用 f 括号,是不是就相当于调用这个 f 函数啊?好,那在这里面是不是就相当于去调用我们的这个 in 这个函数啊? 好,调用 in 这个函数之后,我们这边可以什么呢?再反的 number 啊,那边这个内部函数去调用到了外部函数的这个变量了,是吧?好,那这边我们去调用这个 f 其实就得到了什么?得到我们的 return 的 number 的 一百。好,我来运行啊。 嗯,他说这个地方是定义的错误。哦,那这边大家要注意啊,我这边定义的这个印犯了一个什么样的错误啊?犯了一个最不该犯的错误。好,印是什么? 那大家记得否印是吧?好,什么呢?成员表达式是吧?好,我们的某一个变量音什么样的东西?那在里边的话,我们的这个印啊,是我们的关键字是吧?所以呢,我们去直接定一个叫印, 那这时候就给你报了什么错误啊?那叫语法错误,本来应该是什么 in 什么是吧?但是我们出现了什么 define in 啊, retain in 是 吧?所以这边我们要把它改一下是吧?来改个名, 好,它变成了关键字了,是吧?那这肯定是,那这边我们再来运行。 好,你看,这回就得到了这个结果啊,其实大家最疑惑的是什么呢?是这个位置是吧?啊?为什么他直接去加了括号,我就可以去调用这个函数了呢?那这边大家应该还记得这样的一个写法,是吧?啊? define f o, 好,我们在 f o 里边来去执行这个程序,是吧?好,那这边我说了,要想运行这个函数啊,或者咱们叫做规范化的说,咱们叫调用函数,是不是可以直接这样用啊? f o, 那我们这边是不是还可以给这个 f o 改成其他的名字啊?比如说我把它这个改成什么呢?改成八是吧?八等于 f o, 那 咱们调用这个数的时候,一种方法是可以这么调用,另一种方法我是不是可以直接去这样去使用啊? 啊,看到了吧,这两种用法其实是一模一样的,那对照我们上边的这个程序,咱们来看看,那当我去调用了这个 out 之后,好,那这边返回的是什么样的结果?好,那 f 等于一个函数,是不是得到函数的返回值啊? 好,那这边执行了 out 函数,哎,这是执行是吧?执行了之后,那 f 得到的是谁啊?好,是不是内部函数的这个名字啊?那这句话其实就相当于什么啊?相当于我执行 f 等于 fuc in。 好, 所以这边如果我要想调用 fuc in 的 这个函数,我是不是可以直接使用 f 这个函数啊? 嗯,所以这边我们就可以怎么做了,我们可以把这个东西给它省略掉,变成什么 f 等于 out 来调用。好,调用之后我得到内部的函数,好,那边我们再去执行 f 这个括号,是不是就执行了方克方是 in 这函数对应的括号, 当这边去相当于是直接去调用它了,对吧?好,那在这里大家如果觉得这个写法其实 能理解的话,那咱们这边其实是不需要去给 f 进行赋值了,是吧?你看我执行了他复制给 f, 然后再让 f 来去执行,那其实是不是相当于是他执行了两遍,那我们这边是不是就可以把它写成什么呢?哎,我把 out 不 复制给我的 f 了, 哎,咱咋做呢?哎,咱们直接去调用它。好,调用它之后的话,那我这边我就要讲再去调用 f 再来执行,我其实就可以把这个 f 去掉,是吧?变成啥呢?变成我们的 out, 加上两个框, 变成这种写法。好,那这种写法是不是也成立呢?咱们来看看啊。 好,执行,哎,也是成立的。好,那这为什么成立呢?我们执行了外部的这个函数。好,那调用它之后,我们来到了这,是吧? 好,那这边我们的第二个括号相当于什么呢?再把它再去调用一次。好,那这边调用它,我们返回 number。 好, 返回我们的 b 包作用域里边的一个变量,它的值是一百,所以我可以这样来去写啊。 ok, 我 把它还原回来。啊,这样呢,我们可以做一些参考。 ok, 那 这样的话呢,咱们去看一下,我们这个结论就出来了,是吧?好,第一个,那函数里边是可以去继续使用另一个函数定义的,甚至这个内部的函数定义呢?我可以里边再去定义一个函数, 这边函数里面定义函数的这个方式在 python 里面是合法的,那函数里面定义的这个函数,内部函数是可以使用外部的变量的,这个域的是可以使用 b 包这个作用域的。好,所以这边我们的 return number 也是合法的, 那调用的时候我们希望什么呢?能够调用到内部的函数,它的做法呢?其实就是调用外部的函数,通过 return 直接返回内部函数的一个名称,然后呢,在内部函数当中我再去调用一次,就可以调到内部函数了啊,这是它的一个调用的方法。 好,那么回来,回来之后的话,你就可以知道,那函数内部可以再次定义内部的函数。哎,这就形成了 b 包这个结论是成立的。好,那 b 包作用域呢?内部函数可以仿外部数的变量。好,咱们刚才也试了,是吧?哎,这个定义呢也是成立的 啊,那这俩定义都成立了之后啊,我们接下来就开始去简化这个程序了,因为这个程序大家觉得什么是不是很复杂呀?啊?那很复杂的这个原因是啥呢?那第一个啊,那这边我们去定义这个外部函数加内部函数,我们觉得这个定义和调用都挺不优雅的。 呃,为啥,那为什么要定义这个外部函数和内部函数呢?其实一般是这样的情况,那这个内部的函数啊,往往是先被定义出来的,哎,实现某一些特定的功能, 但是呢,这个程序员开发这个功能之后啊,交给别人,别人觉得,呃,这个功能其实又不太符合自己的一个要求,哎,咋办呢?哎,我这边在这个内部函数外面,我再给他包一层, 哎,这送你的这个心仪的女孩子送礼物一样是吧?啊,那边本来送这个礼物,我这边给他什么呢?给他买回来发现,哎,商品有一个包装,我就这个包装不太好,哎,我想给他加点功能,是吧?我想在上面加个花啊,然后这个这个,这个这个再加一些什么 啊?其他的一些装饰品啊,但原有的这个包装上加不上,哎,所以怎么办呢?我们给他在外面再包一层,再包一层之后,我们再加上自己的这些程序。 好,那对于我们的这个外部套内部函数呢?往往是内部函数先被开发出来,那这边这个开发出来之后呢?我们觉得这个功能并不好,那这边对于内部函数外边我又再包一层,我再包一层外部的函数。 好,那这个包一层外部的函数,虽然包好了,但是你会觉得什么?哎,这定义起来很麻烦,是吧?好,那第一内部函数的这个人呢?咱们要要改这个变量是吧?哎,你看我们的这个 b 包。 好,虽然内部的这个不需要去修改,但是有的时候我们要传入这些值的时候,咱们要变量在外边给它进行赋值,进来,咱们还得改里面的这个程序,是吧?好,那这用的时候你用两个圆括号,是吧? 所以这个时候啊,我们就引入了一个新的这个语法,这个新的语法是什么呢?叫装饰器啊,这个语法,那用什么呢?咱们用这个艾特的这个方式来去实现我们的函数的嵌套。那咱们看刚才那程序咱们可以咋改呢?那咱们可以这样去改啊,那比如说我这边想要做这个事啊, 哎,我想统计一下我这函数运行多长时间。好,那统运行多长时间?咱咋统计呢?我是不是可以写下面这个程序啊,那在里边我们引入了一个叫做 time 啊,时间这个函数库, 那海时间这个函数库呢?其实就是说你只要执行一次,我就显示当前的时间。哎,咱们来去试一下。 好, import time, 点 time 我 直行。 好,这就是什么呢?就是我们当前的一个时间啊,所对应的一个秒数。好,那这边我们说了,咱们执行完之后,这是这样的时间。好,过段时间的话呢,我再来运行它。好,那这个时间的话呢?你看它会发生变化是吧? 好,再来执行。 好,再来执行,你看他会发生变化。好,那这边就是每执行一次,他就取你当前的时间。好,那这时候呢?我就可以利用这功能做什么?哎,取我们函数执行的这个时间了,是吧?好,那我咋做呢?我是这样去做的,音炮的 time 导入我们的这个 time 的 这个功能。好,这功能咋做呢?那 start 等。于。好,我执行一次,我把当前的时间写到 start, 然后我执行函数, 那执行函数之后,这执行结束了,我再去显示一次时间。好,这边我把它记入到 stop。 啊,那边两个时间就有时间差了,是吧?那这时候呢?我用 stop 再减掉 stop。 啊,我这边是不是就可以求出函数一共执行了多少秒啊? 那这个一共执行多少秒,咱们把它再通过 int 转换成整数。好,我这个多少秒就求出来了,是吧?嗯,那这就是我们怎么来去统计这个时间,是吧? 啊,那统计时间的时候,我们说了,除了统计时间,我还得写我自己的函数啊,这是他对应的函数。那这个函数的话呢?啊,在执行的时候显示开始执行,执行结束,那中间的话呢?我这边有一个叫 sleep 啊,就是让程序休眠一秒啊,就是模拟一下,我这程序呢,是执行了一秒钟时间。 好,那这个是啥?这个是我们怎么来去统计这个执行时间的这个功能。好,这里边咱们就会发现了。哎,有啥问题呢?第一个问题是我们写函数的人和写统计时间的可能是同一个人, 那如果呢?你把这个统计时间和函数都混杂在一起去编写的话呢?我们觉得会污染我们的这个环境, 哎,所以我们最好怎么办呢?咱们把统计时间这事写到一个函数里,那这边去执行函数的,这时候这函数功能再写成一个函数, 哎,变成两个独立的函数。那这样我需要统计时间的时候呢,咱们把这个统计时间的函数给它包装到我们的这个方神这个函数外边。刚才我说的这个包装礼物一样啊,这就是礼物,是吧? 咱们给它外面包装一层,那这样其实就是最好的,是吧?好,所以呢,我们要把这些外边的外围的程序,咱们也给他写成一个函数。好,那这咋写呢?咱们就可以这样来写了。 好,那内部的函数,我们把它这个方式啊,给它改成叫做 work。 好,这个 word 是 什么呢?我们来去定义这个函数啊,显示函数开始啊,执行一秒,函数执行结束,那调用它的时候呢,我们就直接使用 word 就 可以调用了, 那我们想统计它,我想给它外面包一层。哎,这咋做呢?咱们其实就可以定义叫做 time it 这样的一个函数。 好,那定义这个函数的时候,我怎么来把这个函数包在我的这个 word 这个外边呢?直接使用一个叫做 at 这样的一个语法堂,后边跟着我们的函数的名称 啊,使用这个 at 跟我们的这个函数名称,那这样的话, python 自动就会把它包装到我们整个这个函数外边。那咱们大家会发现什么呢?调用的时候咱没有去调用外边,是吧?直接去用了里边的这个 work 了。好,那外边这个东西 咋写的?好,他是这样去写的,你看他是不是就是函数里边套着函数啊?好,他怎么写呢?是这样写的啊? define time it 好, function c 这是一个虚拟的参数。好,这个参数的话呢,就会通过我们的这个装饰器法把这个函数的名称, 装饰器的语法会把这个函数的名称传递到这个地方来去做参数,你的那个 word 函数就作为这个参数了, 那然后呢,接下来里边的话呢,就是装饰器的这个执行的过程了。好,这个执行过程啊,计算开始时间,把被装饰的这个函数让他在这运行, 然后直接执行时间结束,然后再去输出。好,一共执行了多少秒?那这边我们再去执行,完成之后的话呢,咱们要注意这个外层装饰器函数,咱们要 return 返回内部函数的这个名称, 然后使用这样的一个形式啊,这个形式的话呢,咱们才会去调用到内部的这个函数,才会去调用到它。好,把我们的药装饰作为名称啊,这个装饰器里边内部的函数作为返回值, 然后里边去编写要装饰它的这些代码啊,那这边我们的这个 function 在 这边传进来进行调用啊,就是这样的一个写法。好,那咱们拿代码来去看一下。 好,这是我们最基础的这个啊,统计时间的这个代码,我先给你运行一下, 运行完之后的话呢,那在这边我们对我们的这个呃程序啊,进行一个相应的输出,好执行。哎,你看这边我们显示函数开始执行,执行结束啊,执行了一秒,我们再来运行它, 执行了一秒,是吧?好,那这边大家看这是怎么写的呢?那在这边我们去执行的这个程序啊,大家注意啊,我们用了装饰器之后,咱们去看执行程序还是原始的这个 word 程序 啊,执行的程序没有变啊,因为上面是什么呢?是咱们说叫做包装作用是吧?好,那这边包装了之后,你的这个程序里边就发生了变化了,是吧? 啊?包装了程序啊,那这包装之后我们执行的还是原始的程序,但是原始的程序的话呢,我们说了,除了啊,这个正常执行的时候,那这边我们还什么呢?增加了执行统计功能。来,咱们看这个 word 里面没有执行统计,是吧? 那执行的统计功能是在哪?是在这个 time eight 啊,这个对应的函数里边。那这边我们的这个 time eight 怎么来去编写呢?第一个它的参数,那要传递我们的函数的名称, 那内部的函数要作为我们的返回值来去使用啊,那执行的时候其实也是先执行它,然后我们这是定义啊,不会被执行。那这边到这 啊, return wrapper, 好, wrapper 被执行好,执行了之后发现,哎, wrapper 函数在这边开始运行,统计开始执行的时间, 好,然后统计什么呢? function c 啊? function c, 你 看这边是作为我们外部函数的一个参数好,这边是不是内部的函数使用到了外部函数的一个变量, 那内部函数使用到了外部函数的变量好,它就是一个 b 包的逻辑,是吧?然后在里边我们进行函数的一个调用啊,就是调用谁啊?我这个虚拟的这些函数啊。 work 好,执行完成之后,那这边通过开始时间和结束时间,我就统计了他行了多少秒了,好,得到什么呢?我们这个函数一共执行了多少秒?这个其实就是一个装饰器,那我们把它改成装饰器之后,大家看一下和我们原的这个代码, 哎,和我们的这个当前这个代码比较起来,大家会发现有一些什么样的区别 啊?那这边大家会发现,第一个区别是我们在编辑这个程序的时候,我们去进行时间统计的代码已经被变成代码,是吧?之前是零散的,分散在整个程序当中的,这会变成了一块独立的这个功能了。 那第二个我把它定义成函数之后的话,我除了统计这个 word 函数,我想统计其他函数的时候,我是不是也可以使用这个装饰器, 再加上 time it 这个函数,我就可以去装饰函数了。好,那对其他函数来讲,我们有啥好处呢?其他函数不需要去改变它的执行方式, 你看它执行方式也不是说来去统计时间是吧?而是就执行原本的这个程序,我们的 python 自动去根据这个语法来去调用上面的 time it 函数。 好,那这个呢?其实就是什么?就是我们的装饰器的好处,那这样的话可以让我们的这个呃,编辑业务逻辑的人专心去编辑业务逻辑。 那当其他人啊,如果需要说是,哎,我需要改变一下程序里边的这些运行的逻辑的时候,我可以什么呢?通过这个装饰的方式来去给它外面去打上包装,是吧?让它变成其他的这些功能。好,那我就把它包装成一个什么可以进行时间统计功能的这个函数了。 啊,你看这就是时间统计功能啊,那这个其实就是就是我们的装饰器啊,他的一个好处。但是呢,我们知道装饰器他的用法里边,那第一个就是我们的内部函数,用了外部函数的变量啊,这个叫做什么?我用了 b 包的这个作用域了。 那第二个呢,咱们要注意再去编写这个器的时候,那你的函数的参数一定是你对应要去装饰的函数的名字 啊,那后边的话呢,你要去调用它的,对吧?好,那还有那这边在外部的这个函数,它的返回值一定是内部函数的名字,那这样的话呢,他才去执行它的时候,才会去通过 read 找到我们的内部的定义,才会去运行的。 好,那这个是什么呢?是我们的这个装饰器啊,必备的一个语法。好,那其实在我们的这个 python 当中,其实系统会内置了很多装饰器,那这些装饰器的话,你们可以去合理的使用它,比如说我们的这个 l r u catch l i u 开始,那这边它可以在你去进行这个迭代的时候给你程序增加缓存,那还有一个呢,比如叫做什么呢?叫做 at reps。 好, 那这边它中文被装饰函数啊,是保持原对象不变的,这个呢,我给大家去解释一下, 咱们可以看一下这个啊,咱们虽然是调用了这个 work 啊,看起来咱们这是没变的,实际对于 python 来讲,我们的这个 work 早就变了,变成谁了呢? 咱们可以看看。好,对于这个 python 的 程序,其实他早就已经变成了谁啊?变成我们的这个 rapper 这个程序了,是吧?好,那这个时候呢,对我们后续去编辑程序的时候是会有影响的啊,那这边比如说我再想取这个对应的啊函数名称的时候,哎,你发现他其实已经变了, 是吧?那这时候我们可能会有印象,那这边我们希望什么呢?咱们取这个名字的时候,还能回到我们的原有的这个 work 这个名字啊,那这个时候咋办呢?咱们就使用这个 wraps 啊,这个装饰器咋做呢?这样做啊啊? 音炮 fuc tools, 那 导入的之后,我们去使用装饰器的时候呢,咱们其实要使用 fuc tools 点啊 啊,咱们要使用 f u n c tools 点 rap, 是 吧?这个装饰系,但这样去写的时候呢,那我觉得是很长啊,所以这边的话呢,我们一般导入的时候,其实会这样去导入 from function tools import 啊, wraps。 好, 那这样去装饰的时候,其实就可以直接去使用这个 wraps。 好, 那它装饰谁呢?哎,就是装饰我们的装饰器里边的这个内部的函数,哎,这样来去装饰好,我们来去运行 好,装饰完成之后呢,咱们可以看看。哎,错误在哪呢?错误在这儿。 好,那这个错误啊,我们虽然没有学过他,但是在里面我已经教过大家了。好,那这个函数里边如果缺少一个必要的这个参数,啥意思啊?就是说这个函数必须得带一个参数,但是我们现在呢,发现没有带参数,是吧?啊,那咱们可以预测一下 啊,这个函数应该带什么样的这个参数呢?哎,那这边我们说了,咱们这个里边啊,我们是使用了这个 fuc 这个函数的,那他呢,必须得什么从我们的这个外边的函数给传进来是吧? 啊,那外边的这个参数其实已经被什么呢?被我们的 rap 来去给隔断了是吧?好,所以呢,我希望应该什么把这个 fuc 从外部给他传到这个位置, 哎,给它传到这个 raps, 然后再从 raps 啊,再给它传到我们的内部的这个函数里边,那咱们可以去通过这样的方式就依次传递进来了。好,那所以呢,我们说这个 raps 我 们在执行的时候它报错了,那报错呢,我们就给它增加一个啊, function c 是 吧?再来运行 好,我们成功执行了,成功执行之后,我们这边再去看一下这个函数它现在是不是假的名字的,哎,它现在是不是还是我们的 rapper 呢?我来运行好,你看它什么呢?就已经还原回我们最开始的这个状态了。 好,所以呢,我们会发现什么?有的时候我们经常在别人的这个程序里看到,哎,我已导入一个系统的 raps, 然后我们的这个内部装饰器啊,我使用了一个 at rap, 再加上我调用的内部函数的这个名称。哎,这是啥作用呢?就是我防止啊,装饰以后, 哎,你的这个原始的函数名称被替换,那我后边程序如果再想使用这个名称的时候呢,就有可能会出现问题了,所以我再怎么办,再给它换回来,是吧?让它的这个在系统内的名字还是自己原有的名字啊? 好,那这个呢,就是我们的系统自带的这个装饰器的功能。好,我来给你总结一下关于这个装饰器的这个概念啊,那装饰器呢,其实在 python 里面是属于比较难的一个概念啊,因为什么呢?它在里面第一个会引用到我们之前讲的 ledb 这个叫变量作用域的问题,那变量作用域呢?因为我们装饰器是函数内层再欠到一个函数,所以呢,这里面会涉及到外层函数的这个参数,以及外层函数的变量,内层函数使用到的这个情况 啊,那这边第二个呢,我们的装饰器的这个写法,其实会打破我们对函数原有的一个认知啊,那这边我们在执行的上是执行我们的被装饰函数啊,那这边我们在装饰函数当中呢,咱们又要考虑函数的参数,咱们又要考虑什么?我们的这个返回值 啊,那返回指导的话呢,是装帧器里边的内层函数的名称啊,所以这个呢,会造成我们学习装帧器的一个比较困难的一点,但是呢,装帧器其实是拍摄里面非常灵活也非常强大的功能,对于这个功能的话呢,我建议大家可以多去执行几次我们的这个演示的这个代码,哎, 你可以把这个呃代码遮住啊,然后自己来去根据自己的理解来去编辑,不断的去调试这个装置器的代码,那这样的话你才能逐渐去理解这样的一个概念。

这个视频将介绍文件流操作的基本步骤。文件流的操作通常包含三个基本步骤, 第一步是创建文件流的实体对象,并且打开文件所对应的代码,如这里所示,或者我们也可以先创建文件流的实体对象, 然后通过调用成员函数 o 本打开文件。 第二步是不断进行读和写的操作,在读写完毕之后,最后一步一定要关闭这个文件。关闭文件可以通过调用成员函数 close, 另外文件流类的 c 构函数也会自动去调用成员函数 close, 从而关闭文件。 这里总结一下文件流操作的基本步骤。首先创建文件流的实体对象, 然后用这个实体对象打开文件,接着进行文件的读和写操作, 最后关闭文件。在关闭文件之后,可以继续用这个实体对象再次打开文件,再进行文件读和写操作,再关闭文件。 在文件处理之后,可以调用希购函数希购文件流的实利对象。在创建文件流实利对象的同时,我们可以打开文件, 同样在希购文件流的实利对象的同时,可以关闭当前打开的文件。 这里特别要注意一下,同一个实体对象一次只能打开一个文件,也就是说一个实体对象在没有关闭文件时, 不能继续用这个实体对象打开其他的文件。另外也要注意一下,同一个文件在任何一个时刻 只能由一个实力对象打开。 也就是说,如果已经有一个实力对象打开这个文件,那么在关闭这个文件之前,我们就不能用其他实力对象再来打开这个文件,因为在打开文件时 通常会给这个文件加上锁,所以其他实力对象通常是无法打开这个文件的。因此我们在打开文件之后, 并且在处理完数据之后,一定要关闭这个文件,如果我们不关闭这个文件, 那么其他实力对象都无法使用这个文件,除非我们关机重启。 这部分内容就介绍到这里,感谢您的支持。

泰森六十天从零到精通第三十四天,函数传餐哈喽,大家好呀,我是钱。上一期我们学会了定义函数,比如写了一个夸夸函数,每次调用都会说你真棒, 但他有个小问题,只能夸你,不能夸朋友、老师,甚至不能夸一只刚学会握手的小狗。不管谁来,都是同一句话。 这就像一台只会做火腿三明治的机器,你想吃鸡蛋的、牛油果的还是培根,他通通听不见,他只给你火腿。 显然这不够聪明,也不够实用。我们真正需要的是一台能听懂指令,按你要求调整内容的智能三明治机器。 在编程中,让函数变得灵活的关键就是常餐,也就是在调用函数的时候,把你想用的具体内容告诉他。 想象一下,你在点餐时对三明治鸡说,我要一份鸡蛋,三明治机器听到鸡蛋就知道中间该放什么。如果你改口说牛油果,他就立刻换成牛油果。这种你给信息他照做的能力,正是通过参数实现的。 具体来说,我们在定义函数时,会先在括号里写一个名字,比如叫食材。注意,这个食材并不是某一种具体的食材,而是一个变量的名字,它代表是我们等会要加进去的东西。 等到真正使用函数时,你再把实际想用的内容,比如鸡蛋、牛油果传递进去。函数一运行这个变量,也就是食材就会拿到你传进来的这个值,并且在函数内部使用它, 从而完成了一次个性化的操作。这里有两个容易混淆但非常重要的概念。第一个行餐是你在定义函数时写在括号里的名字,比如食材,它只是一个接收信息的标签,本身没有具体内容, 用式,预留一个位置,表示这里将来会收到一个值。第二个时餐是你在调用函数时真正传进去的具体数据,比如鸡蛋、芝士或辣条, 这才是函数实际拿来用的内容。举个例子,我们定义了一个做三明治的函数,那这里的食材指的就是我们的时餐, 那函数内部的食材就等于鸡蛋,那这个输出的结果就是正在制作一份鸡蛋三明治。那当我们把鸡蛋换成了牛油果,食材就变成了牛油果, 输出自然就变成了正在制作一份牛油果三明治。同一台机器,一套流程,只因传入的参数不同,结果就不大一样。参数就是函数和外部沟通的桥梁, 没有它,函数只是台只会做火腿三明治的老机器,有了它,立刻变身懂你口味的智能助手。接下来我们一起来动手操作一下吧。首先我们来定一个函数,给它起个名字就叫 make food。 这里的 d e f 表示我要定一个函数, make f 的 就是函数名,意思是制作实物。重点在括号里的 f 的 它不是具体的食材,而是一个参数,你可以把它理解成顾客点单时告诉我的内容, 现在 f 的 还是个空名字就像一张空白的订单,等着别人调用函数的时候,把真正的食材填进来, 接着按下回车,就可以写函数内部要做的事,也就是告诉机器拿到食材后该怎么做。我们可以利用 print 打印一句提示, 这里用到了 python, 你 的 f 杠 shift 这个知识点花括号,你的赋的就自动替换成你传进来的这个值。比如传递了鸡蛋, 那这句话就变成了正在制作一份鸡蛋三明治。好啦,函数写完了,关键就在于那个负的形参,它让整个流程变得可变可定制。那假设今天早上你特别想吃鸡蛋三明治,那就这样去调用它。 注意看,我们在调用函数时,是把鸡蛋作为食材传递进去,这个时候函数内部的负的就等于鸡蛋。我们来运行看一下这个效果,可以看到它的输出结果时,正在制作一份鸡蛋三明治。 那如果明天你心血来潮想换点健康的,比如牛油果三明治呢?完全不用改函数,只需要再调用一次换个食材就行。 这次传的是牛油果,函数内部的负的就变成了牛油果,那这个输出自然就跟着变,那这个结果就是正在制作一份牛油果三明治。 你看,同一个函数不同的输入就能做出完全不同的三明治。我们没有为每一种口味都写一堆重复的代码,而是通过一个小小的参数,让函数拥有了理解你需求的能力,这就是传餐的威力, 他让函数从只会背台词的机器人变成了真正为你服务的智能助手。不过现在的三明治机还只能做三明治,做完就结束了,没法把做好的三明治交还给你进一步使用。 如果函数不仅能做事,还能把结果教回来,那他的能力就更强大了。下一期我们就来解锁函数的回程技能返回值,让你的函数不仅会做,还会给我们,下期见。


十行代码学会解偶在开发 mcu 程序做 bsp 和 app 分 离时,驱动层代码里需要调用某个应用程序的接口,直接调用的时候会藕合严重,而且维护或移植时上层接口不兼容会导致异常。针对这个情况,我们可以通过注册回调函数来解偶。 为了解偶在 motc 里调用串口一的发送 api usc r t print f 时,这是使用场景不直接调用,而是在 mini 注册回调。在 moto 发生回调代码如下。

今天来讲三个比较有启发性的 c 加加的教学案例,这些案例作为问题,在以前的短视频里面都提出过,但是没有给出答案,今天就给出答案吧。 看,这里有个 g, 类 a 有 虚函数 f, 这有一个派生类 b, 他 也有这个虚函数 f, 那 么接着往下看啊,这里面有类 a 和类 b 的 对象,这个类指向 p 的 对象。那么这条语句按照多肽的规则,我们知道它的输出应该是 b 啊,因为调用的是类 b 的 这个虚函数 f。 那 现在我提出的问题是在这边填一条语句啊,不能再有分号了,已经有分号了啊, 使得这条语句的输出结果变成 a, 是 不是看着很奇怪?那我们知道 c a 加里面的多肽是通过虚函数表来实现的, 每一个包含虚函数的类的对象, 它的开头的四个字节或者八个字节,就是一个指向虚函数表的指征。因为多肽的实现,比如说这一条语句通过积累指征,去掉用积累和派生类里面都有的同名虚函数的时候,为什么会实现多肽?就是因为编 辑生成的代码包含了一个查表的过程,就是到这个指征所指向的对象里面,取出它的头四个字节或者八个字节,把它当做指征看去找到了 这个对象所属的类的虚函数表,然后在这个虚函数表里面找出那个虚函数的地址,去调用调用它。鸡类和派生类的虚函数表显然就不一样,那鸡类对象和派生类对象里面这个这个虚函数表的指向肯定也就不一样。总之,为了实现多态,对象里面被多放了虚函数表的指向,这是空间的代价, 那实现函数调用的代码又多了一个查表的过程,这就是时间的代价,但这些都是值得的,因为它能够节省开发的代价。那知道了上述原理以后,我们这个问题就好解决了。我们只要想办法往这个对象 b 的 开头 去填进去类 a 的 虚函数表的时针就可以了,来个 memory copy 哎。把 a 开头的一个指征大小的那部分的内容拷贝到 b 的 开头,那 b 对 象的 开头的几个字节就和 a 对 象开头几个字节一样了,也就是说它们都指向了相同的续航速表,也就是类 a 的 续航速表, 结果这一条语句就会去查类 a 的 虚函数表,找到的 f 就是 类 a 的 f, 输入结果就是 a 了。那讲这个例子的目的不是为了炫技,而是为了让同学们能够看到虚函数表确实存在的一个证明,对这个多肽的虚函数表的实践方式就会有更深刻的认识。 第二个案例是为了证明 v 是 指征的存在,这个案例问的是有个 hello 函数输出这个 hello, 然后呢?这边 a 型 p 是 一个空子帧,然后通过空子帧调用 hello, 问结果会怎么样?当初好多评论区就会说,这老师水平真差,提出这个问题,这明显就是没有定义的行为,怎么能这样写程序呢?我当然不是鼓励大家这么写程序,我写这个案例的目的只不过是让同学们能够比较具体的感受到历史帧的存在。 你要提到使用控制帧就是未定义行为,说这是私加加的文档说的,那就是死读书了。 c c 加加和 java、 python 的 解释型语言有一个重要的区别就是 c c 加加是给程序员完全自由的,程序员想干嘛就干嘛, 程序员想访问控制帧指向的地方就可以访问,想执行数组业界的操作就可以执行。只要程序员不要在编辑的时候特地加选项去限制自己的这种行为,编辑器是可以不管的,会忠实的生成机器指令, 至于这些指令执行的时候会怎么样,是操作系统和程序员自己的事情,和编辑器是没有关系的。像拍审就不一样了。那数组月界的访问这种操作肯定是做不了的,会引发异常。加法,程序使用了控制针也会引发异常啊。加法虽然有按 safe 类可以做数组月界的操作, 但是这么干呢,通常也没什么意义,而且又做起来又很麻烦。比方说像这个程序,我们看上去这个屁是一个空子针,它等于零,然后往空子针所指向的地方写一个数,看上去这个行为是没有定义的,但其实不是。呃,这两条语句的作用非常明确,不是什么没有定义,就是往地址零处去写一个数。 那至于往地址零处写一个数,能不能成功,那是操作系统决定的,那编辑器不需要管这个事,所以编辑器你只要不指定选项限制这种什么对控制能访问的这个行为,编辑器是能够忠实的生成相应的这些机器指令的。这条语句就是往地址零去写这个, 一个数十六进至二十二,那地址零就是放在这个 i x 继承器里面的,所以这段程序并不是什么没定义的行为。这两条语句的意图非常明确,不是什么没有定义,就是往地址零处去写零 x 二十二,而编辑器也能够忠实地生成这些个机器指令, 至于执行起来会怎么样,那是操作系统的事,这个编辑器是不需要管的。回到这个程序,只要你不要在编辑的时候刻意加一些选项,或者是莫名其妙的优化,那这个程序的运行结果就是确定的,它的结果就是会输出 hello, 不 会有任何问题。为什么呢?啊?这就要讲到 一个类的成员函数的本质上是什么了啊?这一个类的成员函数,只要是非静态的成员函数,本质上它是这样的。就像这个 hello, 它本质上是一个全局的函数啊,这全局函数是带一个 zeros 整数的 p 键号 hello 这个调用语句,本质上它是什么样的呢?本质上就是 hello p, 那 你想想看,这个 p 是 一个空子帧,那么以空子帧为参数调用 hello p 进去以后就是 excel, hello 根本就没有用到 zeros 这个空子帧,它怎么会出问题呢?它是百分之一百不会出问题的,只要你在编辑的时候不要乱加优优化或者乱加限制的选项,这个程序 出来的结果百分之百就是哈喽,不管哪个编辑器都这样,所以这段程序不是什么未定义的行为。如果哈喽这个函数面用到了这个成员变量呢?那情况就不一样了,因为这个时候这个全句函数本质上就这样,这里面就用到了类似 借号癌,那这个类似是一个空子帧,那么这条语句执行起来会是什么后果?就取决于操作系统了,那通常就会导致程序的崩溃。

传统的 r a g 早就已经过时了,二零二五年是 a 阵的元年,不会 a 阵的 r a g 的 大模型工程师,就像不会写函数的程序员一样,看似在干活,实则已经被时代淘汰了。 今天我们就从最基础的 r a g 开始讲起,一步一步带你揭秘它是如何进化成 a 阵的 r a g 的。 下面我们先来看一下他们的基本概念啊,什么是传统 reg 以及什么是 agent 个 reg。 那首先的话就是我们的传统 reik, 那 传统 reik 的 话,我们可以把它分成两个部分,第一个部分呢就是文档的切片,然后到向量化再到存储。第二个部分的话,是用户的这个问题啊,然后去进行剪辑,然后拼接我们的 prom 的 提示词,再到我们大模型的生成,那么这个过程相信大家都已经非常熟悉了, 那么在这里啊,我们快速的把它过一遍啊,第一个部分的话,也就是我们从切片到向量化存储,那它具体啊,指的是什么意思啊?那首先的话,我们需要有一些自己的文档,那你想要去做知识库,那你肯定有需要的这样的一些数据,那可能包括像 pdf 啊,像 word 啊等等。 那有了这些文档之后啊,因为它并不能够一次性全部给到咱们的大模型,那里面的文章或者内容可能会有很多,比如说一万个字,两万个字, 所以啊,我们需要来对这个文档来进行一个切片,那切片之后我们就能够得到一个一个的段落,每一个段落它的长度啊是两百五十六个字符,那互相之间啊,可能还会有一定的这个重叠,那对于每一个段落,我们需要用一个 embedding 模型来进行向量化的这样一个处理, 向量化之后啊,我们就能够得到一个固定维度的这个数值,那么这个数值啊,我们也叫做向量,有了这项量之后,我们再把它存储到对应的这个向量数据库当中来, 然后接下来我们就会来进行解锁和操作,解锁也比较简单啊,用户的问题拿到之后啊,我们也是用同样的 embedded 模型转换成像量,然后在向量数据库当中啊去计算它们的一个相似度的一个分数。比如你的这个向量数据库里面,它可能有十个片段, 那么到底哪一个片段和用户的问题,它的相似度是最高的呢?那么最简单的方法就是我们可以去做两个向量的点击,然后就会得到一个值, 那么值最大的那其实就是我们最相关的这样一个文档,然后我们就可以把它塞到咱们的这个 prom 的 提示词里面来,那么在这里啊,我们可能就能够通过这个 similarity 啊来进行这样一个 啊相似度的这样一个计算。那么传统的 red 啊,我们有两个链路啊,其实我们刚刚讲过的那第一个就是从文档的这个切片,然后到向量化存储的这个过程,那我们可以把它叫做离线链路啊,这个离线链路是什么意思呢?那么在这里啊,给大家重点的我们来看一下啊, 首先啊,你有了对应的这样一些文档之后,那我们就可以通过咱们的 embedded 模型来进行向量化,存储到咱们的向量数据库当中来。好,那这是我们的第一步,这个部分啊,其实是一个离线的,因为它和用户啊,其实是没有任何关系的 好不好?好,那么在线量度的话,就是我们刚刚也有提到过的,就是解锁,然后拼接咱们的 promenade 词,然后再到我们这个生成,那么我们来具体啊,看一下它到底是怎么去做的啊?那如果一个用户,哎,他在提出问题之后,那么首先我们要看的是哪里啊? 好,是这个啊, user query 啊,这是我们的用户。好,那么用户首先我们会提出咱们的问题,有了这个问题之后啊,那么首先第一步啊,我们就要来对我们这个用户的问题来进行一个改写。 好,这个地方可能有同学会问啊,为什么要去做改写了?那么是因为用户的问题在很多时候啊,他并不一定能够符合我们的一个呃,数据库的这样一个需求, 所以我们可以对问题啊来进行一些改写,那更适合于咱们的这样一个解锁。所以改写完成之后,那么首先我们可以用第一种方式啊,比如说 bm 二五的方式来进行第一次解锁,那么我们就能够得到我们对应的这样一些相关的片段,比如说 document 一 二三,那拿到这三个片段之后, 接下来我们同样的再用 embedded 模型来对我们改写之后的这个 query 来做一个向量化,然后再通过这个向量相似度的这个匹配,来从我们的这个向量数据库当中啊,再来做一次解锁,那么我们又能够去返回什么?又能够去返回一批这个 document, 那 么拿到两批 document 之后啊,接下来我们来对它做一个这个合并啊,做完合并之后,我们再来进行一个重排序的一个过程, 比如说这个 b m 二五,你能够拿到三个片段,那么我们的向量的相似度也能够拿到三个片段,那么合并之后就是六个片段,我们再对它来做一次重排序啊,重排序之后,我们再把最终的这样一个片段的一个结果给到咱们这个 promet 里面来。那么这里的 promet 啊,其实也很简单啊, 比如我们就可以写什么呢?这个地方啊,我们来看一下,我们可以写一个非常简单的啊,比如这里面有一个 prompt 啊,一个 jump, 那 么比如你说对吧?你是一个专业的助手,对吧?请根据下面的问题和解锁回答这个文档来进行回答,那在这里的这个 context, 对 吧?就是我们解锁回来这个 document e 到 n 的 这样的一个文档片段啊,那么你可以去自行进行设置啊,比如说这个 top 十哎, top 三, top 五啊,都可以, 然后再加上咱们的这个用户的问题啊,这个 question。 好, 那有了这两个内容之后啊,模型就能够根据我们这个 promontor 来进行最终的这样一个回复, 这就是传统 reg 的 运行流程,整个过程啊,是单向固定啊,并且是一次性的。首先从用户的问题进行解锁,然后拼接相关的这个上下文信息啊,给到我们的 promontor 题词,然后再交给我们的大模型啊,去进行最后的这样一个答案的一个生成。 但事实上有的时候啊,我们的第一轮解锁,它其实并不能够得到我们想要的内容,所以模型啊它可能想要重新去解锁一遍,或者换一种方式去解锁,甚至说我们用某一个工具啊,去获取更多的上下文信息之后啊,再来做最终的回复。那么传统的 reg 是 无法去实现的, 所以我们就需要用到咱们的啊 agenticreg, 那 么它到底是什么呢?其实 agenticreg 是 对整个流程的一次根本性的升级, 那么它是把整个 read 当中啊,各个环节,比如说 query 的 改写也好啊,包括向量的剪辑,以及我们这个关键词的搜索等等,全部都封装成可以去调用的这个工具也用的这个 tools, 并且允许啊大模型在生成答案之前,能够去进行自主的决策,多轮调用以及动态的调整。 换一句话来讲, agent 它不再是一条直线走到底,而是一个可以循环的智能体行为的闭环, 从思考到调用工具,到观察结果,然后再思考,再行动,直到生成最终的答案。这就是 agentic 的 概念,把解锁能力工具化,让模型啊能够通过啊 to user 的 方式啊,去循环地去调用这样一些工具,获取到完整的信息之后啊,然后再返回相关的答案,那这就是 agenticreg, 好,那么它的整个运行流程啊,其实和我们刚才讲的前面的话基本上是一样的,对吧?首先还是用户啊,咱们 user, 那 么它会产生一个 query 啊,产生一个问题, 好,那么有了这个问题之后啊,那之前我们可能直接啊,就把它向量化,然后去进行这样一个相似度的一个匹配了。但是现在我们会直接把这个 query 啊,然后交给咱们的大模型,比如说 gpt 也好, graph 也好,甚至说 dbic 好, 那么 交给它干嘛呢?那么由模型自己啊来进行判断需不需要去调用咱们的工具,那么这个工具啊,它可以是我们本地的工具,也可以是 m c p server 的 工具。总之啊,调用完成之后,那它会给我们返回一个什么呢?比如说我们这个 to result, 对 吧?就返回一个结果,要给到我们这个模型, 那模型就可以根据这样一个结果,然后去决定下一步到底是继续的调用工具,还是说根据已有的结果,然后去返回对应的答案。那这就是它整个的一个流程,那么 agentreg 它的一个实现啊,主要是依赖模型,它需要去具备啊以下的三类核心能力。 第一类的话就是我们这个 pruning 啊,也就是我们这个规划的这样一个,就是规划的能力啊,比如说要规划怎么去做,然后反思结果怎么样。那么这个我们一般的话是体现在咱们这个 run c o t think 的 这样一个过程当中啊,也就我们整个推理的这样一个过程当中。 然后第二个核心能力啊,就是我们的一个工具调用啊,或者说多 agent 和协助的这样一个能力,那么这个我们是体现在工具调用的这个机制里面的。然后第三步,也就是我们模型啊,它可以在回答最终的答案之前,能够去进行多步的工具调用,那这也就是我们所谓的 agent 口的概念 好了,那么到这里啊,相信你已经理解了他们之间的区别了,传统 reg 其实就是固定流程解锁一次,深层一次结束了,那 agent reg 它是等于智能体的行为,它是可以去规划、调用、反思以及迭代的。 理解了这样的区别之后啊,我们接下来就能够去迈向更关键的大模型应用开发。好,接下来的话,我们再来看一下咱们具体的代码实现的能力的。 首先的话,我们先来讲一下就是传统瑞克它是如何去实现的,那整个的流程啊,我们可以参考咱们右边的这张图,也就是我们前面讲过的 离线的这一部分和在线的啊,这一部分的内容,那因为代码也比较简单啊,所以我就带着大家快速的去过一遍。那整个内容的话,核心啊,我们是通过廊桥来实现的,所以大家可以直接参考这一部分代码啊。 首先第一步的话,就是我们要去加载咱们的这个文档啊,比如说你觉得这我们是一个 txt 的 一个文件,那大家的话可以把它替换成你要去处理的文本啊,比如说像这个 pdf 啊, word 啊,都是可以的,它也支持啊。 然后这个 test loader 啊,那么它到底是什么意思呢?其实就是把我们这个文档加载到咱们的这个内存里面来,本质上其实就是一个读取文件的操作啊,你可以理解为咱们这个 read file, 好 吧,一样的啊, 好,那么文档加载完成之后啊,接下来我们对文档来进行切分啊,因为一个完整的文档比较大,所以我们就会用到这个 longchang 里面的这个地归分割的这样一个方式啊,那它的逻辑也非常简单,我们只要去设置两个内容, 第一个也就是我们每一个文本块的一个大小,比如说你要设置成一百个字母,两百个字母还是五百个字母啊,都是可以的。然后第二个就是文本块之间的一个重叠的一个部分,那其实就是我们前面有讲到过的 啊,这个段落和段落之间啊,可重叠的这样一个部分,那有了这两个参数之后啊,那我们就能够拿到对应的这样一个 段落了啊,比如说一篇小说啊,这个一张小说,那可能现在就已经被切分成三十个段落,那对于每一个段落,哎,接下来我们都需要通过一个 embedded 模型来进行向量化,那这个 embedded 模型怎么来的啊?在这里我用的是一个 啊归机流动的这样的一个服务啊, ok, 然后模型的话选择的是千万三 in body 啊,零点六 b 的 这样一个模型,那接口的话就是用这个 open i 的, 然后这里面还有一个 api k 啊,那这是我自己的好吧,当然大家的话也不要用我的,因为没有用啊,录完视频之后,我会把这个 k 啊去删掉。那么大家的话可以直接去归机流动啊,自己去申请啊,因为也是免费的啊,它会给你对应的额度。 那么向量化完成之后,哎,接下来我们就需要把对应的这样一个内容啊,然后存储到咱们的向量数据库当中去啊,那么在这里啊,我们选择的是相对比较简单的那个 crala, 那 crala 的 话是一个非常简单易用的这样一个向量数据库啊,那么在这里的话怎么去实现呢?那很简单啊,大家可以看到这就是提供我们对应的三个参数,第一个参数啊,是不里头,那这就什么?这就是我们切分之后的这样一个片段啊,也是我们的段落, 然后 in body 啊,这是我们的 in body 模型,那我们前面去设置好的,然后第三个啊,也就我们这个干嘛?我们这个路径啊,看到吗? ok, 好, 那这个路径的话,我们就设置的是当前的这样一个路径啊,就是那个绝对,我们这边是设置了一个相对的一个路径啊,然后他就去进行一个保存,然后最后啊再来做一个结果的一个打印。好,那么代码我们讲解完之后啊,下面简单的来运行一下,看一下它实际的一个打印。好,我们稍微等待一下啊, 好,那现在我们可以看到啊,最终的一个结果了,那么一共的话是把三十七个文本块啊,全部存到咱们的这个向量数据库当中来了,那么也就是我们的 这个 txt 啊,这个文件,那一共的话是切分成了三十七个片段啊。 ok, 好, 那这就我们的第一个部分啊,离线的这个部分,对吧?我们已经把什么把我们的这个文本数据哎,从文本转换成像量,并且保存到 crmdb 里面来了。 那接下来的话,我们再来看一下在线的这个部分,那么实际上啊,他会比我们这个离线部分会更简单啊,为什么?因为在实际的工作当中啊,大部分的这个我们要去处理的内容其实都在离线列录。 大家想一下,如果说你是一个大模型的这个开发人员,这个应用跟你啊其实是没什么太大的关系的好不好?我们需要去做的就是如何去给模型提供更好的上下文啊,让模型的回答能做的更好,所以它的重点在于如何去切分文档, 切分的方式有很多,对吧?怎么去选择以及如何去做向量化的存储,对吧?我们可以去选择或者微调自己的这个模型,甚至说决定啊是否要使用咱们这个 query 的 一个改写的这样一些策略, 那这样的一些内容,它都能够去影响,最终我们拼接到这个 prompt 的 提示词里面的这个啊, context 的 它的一个质量,所以啊 最重要的工作其实都已经在离线链路里面,我们其实已经把它做好了,所以在这个在线链路里面,那么我们其实去看的话,它的这几部分内容,首先第一个部分还是一样的,对吧?也是去加载咱们的一个 embody 模型啊,这个是没有任何的一个区别 啊。然后这个地方有一个 vector strong, 那 么这个呢,就是加载咱们的 charma db 啊,把咱们的这个向量数据库,你给它加载进来之后,接下来我们去构造了一个用户提问的这样一个 query 啊,因为我这个是一本小说啊,所以就随便提了一个问题,就是韩丽啊,比如他的这个二哥啊,叫什么名字? ok, 好, 那么有了这个 quarry 之后,然后我们来开始来进简所,在这里啊,也是非常简单的,直接去调用咱们这个 similarity 啊,然后设计的这样一个方法, 然后把我们这个 quarry 啊用户的问题传进来,然后这边有个 k 等于三,那么表示返回最相关的三个片段就可以了。 所以我们的这个 doc 啊,那么它的一个结果其实就是三个 document 的 啊,这样的一个文档,然后拿到这个文档之后我们在干嘛?哎,我们在这里啊,做了一个内容的一个读取, 看到吗?哎,这边有一个配置 content, 就是 把它里面具体的文本内容啊读取出来,然后我们把这个 context 拼接到咱们的这个提示词模板里面来了。 ok, 好,那么给到这个题词模板之后,接下来创建咱们的大别模型,然后把咱们这个题词再给到这个模型,让他去生成最终的这样一个答案啊,就可以了啊。所以这个地方咱们这个题词这个模板其实也比较简单啊,对吧?就是如果说你知道,那你就回答,对吧,如果说你不知道,那你也不要这个 胡编乱造,所以在接下来的话,我们就来运行一下啊, ok 啊,这边会有很多的一些提示啊,这个我们可以直接忽略啊,因为这个没有影响。好,来我们看一下,那么首先的话是我们的一个参考的一个文档,好,那么参考文档有三条篇的,这是第一条,然后第二条,对吧? ok, ok, 还有这一条啊,三条,好,然后这三条文档的话,我们大家可以看到,其实都是和这个大哥相关的啊,没有去体现出来这个二哥的一个内容, 对用户的这个问题,那么最终的一个大模型的一个回答给到的是什么?不知道啊,因为参考文档里面没有提到这一个啊,这个二哥是谁?所以其实我们可以发现啊,对于我们真正的这样一套 red 系统来讲,最核心的其实就在于咱们的这个 context 的 上下文, 这如何去给它去构造这一个啊?高质量的这个 context 才是我们的一个工作的一个重点啊。好,所以说我们要跟大家去分享了,就我们传统 reg 的 这样一个实现的这样一个方式啊。 ok, 那 么我们在讲完了传统 reg 的 实现之后啊,接下来我们再来看一下 agent reg, 那 么它到底是如何去实现的? 如果我们在工作当中啊,也想要去做一套 agent 这个系统,具体啊该怎么去做啊?那么大家的话,其实可以去参考啊,其实 box 啊这个软件啊,它是怎么去实现的?因为它是开源的啊,所以在 github 上面也能找到它对应的源代码,它里面其实就实现了这样一套系统啊, 传统的 reg 都是解锁拼接,然后到生成一条龙,直接去输出对应的结果,而 agent 这个 reg, 那 么它是一个 agent 循环的一个过程。好吧,从思考到行动观察,然后再思考得出结论,它是一个多步的一个过程啊,它是一个不断去循环的一个过程, 如果你对思考与行动模式啊,还不太了解的同学,可以去看我之前的这一期视频,那么里面的话详细的去讲解了 react agent 它实现的全部流程。下面的话,我们再来看一下啊,那么切的 box 啊,它的一个实现的一个逻辑啊,那么在这里的话有一张图啊, 给大家来看一下啊,那么在这张图片当中啊,呃,其实就很好的去体现了,为什么签的 box 啊,它在早期啊,就体现了比其他产品呢,就更强的这个 agikrad 的 这样一个能力啊, 那么它整体的实现,它怎么去做的?它是用时间啊去换取了模型的这样一个智能,当用户把问题发过来之后啊,系统首先需要去判断,那么我们的模型它是否支持工具的调用,那如果说不支持,那不支持的话,我们就不用去看左边的啊,直接去看右边就可以了。 在右边的话,我们就通过这个 prompt 啊去判断咱们这个问题啊,是否需要进行解锁来进,需要解锁才能够回答,那如果说我们判断出来不需要,那不需要的话,我们就可以直接去进行回复了,也就直接去跳到这里,好吧, 那我们这个系统啊,直接就给到用户的这样一个回答啊,没有去进行这样一个啊片段的一个解锁,那这种方式啊,它比我们在 prompt 里面让模型去忽略无关的这个 context 的 更优啊,好吧,或者它的效果会更好,因为我们是用两个模型来做的决策,理论上它的效果就会更好, 那如果说我们判断出来的结果是需要干嘛呢?哎,是需要来进行解锁,那么在这里,哎,我们就会先来到咱们的这个 知识库当中啊,去进行这样一个语义的一个搜索,搜索完了之后得到相关的这个文档的片段,然后再把这个相关的片段去注入到咱们的这个 context, 咱们的这个上下文里面来,然后再基于这样一个结果啊,去生成对应的这样一个回答。 好,这我们的第一种情况就是这个模型他不支持工具的调用,那么如果说模型他支持工具调用,那怎么办呢?那这个时候我们会直接把我们的这样一个工具的这样一个集合,哎,全部注册到咱们这个模型里面来,然后让我们的模型啊自主决策去调用哪一些工具,比如说剪辑的工具,对吧? ok, 好,有这个语音解锁的工具,然后还有咱们这个文件列表的工具啊,然后读取某一个文件具体的这个内容的这个工具啊,非常多。我们再把所有这个决策啊交给咱们的这个模型啊,而不是说固定一步啊,要解锁到这个结果啊,那这就是 h 集合啊, 好吧,就它能够更早的去利用模型的能力啊,去提升 rap 的 效果。因为在传统的 rap 当中,我们只有在深层阶段才会用到咱们的大模型。 但是在 agent tech 当中啊,我们从判断开始啊,从我们这个用户输入问题之后啊,我们就会开始去用咱们的大模型来进行决策,好吧,那么对于这样的一些工具来讲啊,比如这边有四个,对吧?好, 如果啊,我们在实际的应用当中啊,那你觉得除了这个 search query 之外,因为这个是我们最基本的一个工具啊,除了这个基本之外,如果你觉得不够啊,那么我们还能够去设计一些辅助的一些工具,比如说这三个。 那 chatbox 里面啊,其实就有这么几个啊,比较有特色的工具。那第一个的话就是这个 list of files, 那 么它的一个作用的话就是能够去列出来,比如说能够去列出来啊,当前这个知识库里面的文件清单啊,它能够去作为一个兜底。 为什么呢?因为传统的 red 它是无法去回答你,比如说你的知识库里面有哪一些文档,这个它能回答吗?不能,因为传统 red 的 本质它是去做什么?它是把 这个解锁到一部分内容之后啊,然后交给大模型来进行回答,但是它的这个内容肯定是不完整的 好吧?所以你问它有哪些文档,它肯定回答不了。但是有了这样的一个工具之后,我们就能够让模型来进行调用啊,并且得到当前的这个知识库里面有多少个文件啊?它数量的这些信息都能够获取到啊?这是第一个。然后第二个还有一个是这个 read file check 啊, 那么这个方法它的这个作用是什么?就它能够按照我们的这个,呃,根据我们这个文档的这个 id 啊,然后精确的来进行这样一个片段的一个读取啊。那么好处的话有两个, 第一个就它能够规定啊,你只读这个特定的这样一个文本片段,好吧,就我把这个 id 告诉你,你就读这个就可以了。然后第二个当它发现这个前后的这样一个 check, 去补充咱们这个上下文, 那么这样的话我们就不需要去依赖咱们的语义的相似度解锁,我们直接通过固定的这个 id, 然后去获取到相邻的这个上下文。那除此之外的话,还有一些,比如说这个啊, get file method 这样一个原读取这个原数据的啊,好吧,当然这个我们用的比较少, 所以这样的一些方法,或者这样的一些工具啊,就是前的 box 啊,它的这样一个实现的一个方式啊,好吧,所以我们在传统 re 的 当中,对吧?可能我们解锁不到信息的时候,哎,我们就直接返回说我们没有找到了, 但是在 agent 这个 red 当中啊,他可能会尝试啊,去改写我们的这个 query 啊这样的一个问题,然后再去解锁啊,或者说用这个啊 read file check, 然后去补充咱们上下文,然后再做最终的一个回复啊。那么在这里的话,大家的话也可以看一下啊,我们下面的这两个案例啊,能够让大家更 精准的去理解啊,这个 agenticreg 它到底是什么意思啊? ok, 好, 那么在这两个案例当中,首先第一个,对吧?传统 reg 就是 用户的查询,然后向量解锁,然后解锁了结果之后啊,然后再生成对应最终的一个答案。 那 agenticreg 它怎么去做的?首先它会有第一轮搜索,那么我们就用传统的这个什么这个 quick research 啊,来进行这样一个搜索 好,然后搜索完成之后,那么结果他可能不是很好,那我们就能够观察到命中率啊,非常的低,所以我们就要去尝试去改写咱们的这一个查询的这样一个词, 然后第二步搜索的时候,那么我们就能够把我们上面的这些问题已经改写过了,然后再去进行查到,那我们就能够命中一些比较相关的这样的一些 tab, 然后再进行第三轮的搜索,哎,去获取一些这个相关的这样的一些文档。然后最后啊,再基于我们这个比较, 对吧?就比较这个已经整理好的一些步骤啊,然后去完成整个片段的这样的一个回复啊,那这就是他们两个之间啊最大的一个区别。

到目前为止呢,我们只是学会了定义一个主函数,随后呢我们还学习了哎,如何使用我们的 print f 函数, 那么接下来呢,我们要讲如何定义一个新的函数,并且呢把这个自己定义的这个新的函数加入到我们的程序中。 好,我们来看一个例子,这里呢我们定义了一个函数叫 butler, 也就是管家函数,这个名字呢,是你自己取,它是个标识符,取这个函数名,哎,这个标识符的名字是怎么取的?有什么规则?之前呢,我们已经给大家讲了,这里呢我们就直接来看 这个函数呢,在我们的程序中出现了三次,大家来看一下,这是第一次出现,随后呢这是第二次出现,下面呢这个是第三次出现,那么我们来看一下这三次出现代表什么意思,哎,第一次出现代表的是哎,函数原型啊,我们把这次出现 称为函数原型,那么它的作用是什么呢?它的作用是要告知告知我们的编辑器,我们在程序中要使用我们这个这样一个 bug 函数啊,这是函数名, 那么第二次呢,第二次呢,是以函数调用的形式出现在我们的主函数中啊,这个地方呢,大家看清楚,它是函数调用,和我们上面这个 print f 啊,它们的地位是一样的,上面这个代码呢,它其实就是调用了 print f 啊这个函数,而我们下面这行呢,是调用我们 bugler 这个函数。 那么第三次呢,出现在函数定义中啊,我们这次出现它有个名字叫函数定义,函数定义哎,是函数本身的哎原代码, 那么接下来呢,我们就依次分析一下,首先呢我们来看函数原型,函数原型呢是一种声明形式啊,它是要告诉我们的编辑器,我们程序当前要使用我们这个 butler 这样一个函数,因此呢,函数原型也有另一个名字,称为函数声明啊,函数声明这个地方挡住了函数声明 好,那么函数原形呢,还指明了函数的函数的这个属性。首先第一个哎,这个地方 v o i d, 大家想想我们之前学的主函数哎, int m a i n 里面里面呢是哎, v o i d 对 不对?那么这个 int 我 们之前讲的是输出或者说返回的时候,这个函数返回一个整数, 那下面这个地方 v o i d, 大家想想哎,结合这个 v o i d 想想它是什么意思呢?其实哎就是告诉了我们当前这个就表示了我们当前这样一个函数,它是没有返回值的啊,没有返回值的, 那么后面这个 v o i d 呢?这个空呢?哎,也就是表示的是我当前这个函数是不带任何参数的,和我们之前讲的这个形式是一模一样的啊,这个 v o i d 大家记住就是空的意思,就是什么都没有的意思,哎,就是这样这样一个形式,这是我们的函数原型, 那么第二次出现呢?我们刚才说了它是函数原型,那么第二次出现呢?我们刚才说了它是函数原型,那么第二次出现呢?我们想要在主函数中调用我们下面这样一个定义的函数, 其实很简单,就是函数名加这个圆括号。上面呢我们看这个函数原形,我们之前说了是没有参数的,对不对?所以说这个地方呢,你就直接加一个括号就行了,里面不需要有任何参数,而我们上面这个 print f, 它里面是需要有参数的啊,而我们下面这个 bugler, 它是不需要有参数的,你使用的时候直接加个括号,直接就是函数名,加个括号,哎就行了。那么当我们的函数,当我们这个程序执行到这一行的时候, 那么它就会执行我们这个 butler 这样一个函数啊,这个函数执行完完毕呢,哎,我们程序会继续执行下一条语句,也就是这条语句啊,直到最后一条, 而这个 butler 最后一次出现呢,哎,其实就是我们的函数的定义,它的形式和我们定义这个主函数是相同的,哎,都是包含 函数头和花括号括起来的函数体的,而函数体我们之前说了,它是由语句和声明组成的,我们当前呢,哎,这个函数呢,没有使用任何的变量,所以说就没有声明,只是由一个哎调用了一次函数,一个语句。 好,那么我们这个函数头呢,它重述了函数的函数原形的信息,就是和我们上面这个信息是一样的,它不带任何参数,而且呢没有返回值。但是大家要注意一下我们这个函数的定义,哎,后面是没有没有这个分号的,而我们这个函数原形这个地方呢,是有这样一个分号, 好,这就是我们这个 butler, 从我们定义到调用,哎,这一整个流程最重要的啊,就最容易忽略的其实是这个 函数原型啊,大家要好好理解一下它,其实呢就是要告诉我们的编辑器,我们接下来的程序呢,就是要使用这样一个函数,这个函数呢,也有自自身的一些特性,比如说,哎,它是什么样的,什么样的参数,没有任何参数,对不对?它返回什么样的类型,没有不返回任何东西,对吧? 好,这里呢,我们再强调一下啊,如果大家使用的是老式的编辑器,那么这里呢,我们就把这个微 o id 给去掉就行了。 那么这里呢,我们还要注意一个事情,我们何时执行这样一个 bug 了函数呢?这完全取决于它在,哎我们这个主函数中被调用的位置,可以看到它是在两个 print f 函数之间,那么因此它, 哎,因此我们这个程序,首先呢先执行上面这样一个函数,这个函数执行结束之后呢,紧接着就执行我们的 bug 了,这样一个函数,那么接下来呢,哎,就依次往下执行啊, 那么有些同学就会想,那我把这个函数啊,这个函数我现在写到最下面,那么我把这个函数,我把这个函数的定义,哎,放在这个主函数之前,哎,放在这个位置, 那么放在这个位置会不会改变我们当前这个函数的,呃,在什么什么时候被调用呢?其实是不会的啊,也就是说,无论我们的 主函数在程序文件的什么位置,我们的 c 程序呢,都从主函数开始执行,那么我们当前这个函数在主函数里的什么位置,那么就什么时候执行这个函数。 但是呢,按照惯例,我们习惯是把主函数放在开头啊,因为它提供了程序的基本框架,而把定义的其他的函数呢依次往下写, 这里呢还有一个小细节,这个函数呢啊是我们自己定义的函数,我们定义的时候,我们说要有函数原型,而上面这个函数它本质也是函数啊,因此呢, 它们俩的地位是一样的,所以说你可以看到它们的颜色呢,都是黄色的,但是有些同学就会想,那上面这个这个既然也是函数,那那么它的函数原型怎么没见呢?其实它的函数原型就包含在了我们这样一个哎头文件里,它包含了我们 print f 这个函数的哎函数原型,那么按照我们的 c 标准呢, 我们要为程序中所有的函数都都提供哎,都提供函数原型,但是呢这些标准库的函数呢,是由上面这个文件哎对应的文件来提供的,所以说我们就我们就我们就没有看到这个函数,它的函数原型在哪,其实是包含在这个文件里的, 那么讲了这么多呢,我们就来实践一下,好,这里呢我们就不一点一点的输入了,为了节省时间。那么按照我们之前的 哎思路呢,首先是要执行这一行,对不对?就是执行到主主函数的时候,首先要执行这一行,随后呢哎这一行 这个字母串打印出来之后呢,紧接着下面就执行我们这样一个函数,而这个函数它的功能呢,其实就是要打印这样一句话,那么这一句话打印出来之后呢,就会接着哎执行下面的代码,以此是这行代码一直到 return 零,然后结束。 那么其实这个结果呢,应该是有三句话的,这是第一句话,哎,然后呢?这是第二句话,随后呢这是第三句话。那么接下来呢?我们来看一下是不是这个顺序,好,我们来执行一下。 好,此时大家可以看出来,第一句话是不是就是这句话?第二句话就是我们这个函数里面的这句话,第三句话呢就是最后这个。