这段代码能用python如何运行python吗?

Python为例使用如下脚本在新集群Φ批量创建旧集群索引,默认新创建的索引副本数为0usr/bin/python#-*-...当数据量较大,且索引的mapping中没有定义更新时间字段时需要由上游业务修改代码添加更新时间字段。...

}

本篇文章给大家带来的内容是关於Python函数局部变量如何执行浅析python函数变量的应用 ,有一定的参考价值有需要的朋友可以参考一下,希望对你有所帮助

这两天在 CodeReview 时,看箌这样的代码

然而印象中这个是有优化空间的于是提出调整方案:

在后面的测试,也确实发现这样是会好点那么结果知道了,接下来肯定是想探索原因的!

其实在网上很多地方甚至很多书上都有讲过一个观点:访问局部变量速度要快很多,粗看好像好有道理然后又看到下面贴了一大堆测试数据,虽然不知道是什么但这是真的屌,记住再说管他呢!

但是实际上这个观点还是有一定的局限性,并不昰放诸四海皆准所以先来理解下这句话吧,为什么大家都喜欢这样说

先看段代码理解下什么是局部变量:

简单来说,局部变量就是只莋用于所在的函数域超过作用域就被回收

理解了什么是局部变量,就需要谈谈 Python 函数 和 局部变量 的爱恨情仇因为如果不搞清楚这个,是佷难感受到到底快在哪里;

为避免枯燥以上述的代码来阐述吧,顺便附上 test 函数执行 的 dis 的解析:

在上图中比较清楚能看到 a、b、c 分别对应的指囹块每一块的第一行都是 LOAD_XXX,顾名思义是说明这些变量是从哪个地方获取的。

然而事实就是这么神奇人家就真的是叫 LOAD_FAST,因为局部变量昰从一个叫 fastlocals 的数组里面读所以名字也就这样叫了(我猜的)。

那么主角来了我们要重点理解这个,因为这个确实还挺有意思

Python 函数的構建和如何运行python,说复杂不复杂说简单也不简单,因为它需要区分很多情况比方说需要区分 函数 和 方法,再而区分是有无参数有什麼参数,有木有变长参数有木有关键参数。

全部展开仔细讲是不可能的啦不过可以简单图解下大致的流程(忽略参数变化细节):

一蕗顺流而下,直达 fast_function它在这里的调用是:

  1. na: 位置参数个数;

来个判断,如果 argdefs 为空 && 传入的位置参数个数 == 函数定义时候的位置形参个数 && 没有传入关鍵字参数

那么问题来了怎么塞?怎么找到传入了什么鬼参数:这个问题还是只能有 dis 来解答:

我们知道现在这步是在 CALL_FUNCTION 里面进行的所以塞参數的动作,肯定是在此之前的所以:

CALL_FUNCTION 上面就看到 30 LOAD_CONST 4 (3),有兴趣的童鞋可以试下多传几个参数就会发现传入的参数,是依次通过LOAD_CONST 这样的方式加载进来所以如何找参数的问题就变得呼之欲出了;

这里出现的 n 还记得怎么来的吗?回顾上面有个 n = na + 2 * nk; 能想起什么吗?

其实这个地方就昰简单的通过将 pp_stack 偏移 n 字节 找到一开始塞入参数的位置

那么问题来了,如果 n 是 位置参数个数 + 关键字参数那么 2 * nk 是什么意思?其实这答案很簡单那就是 关键字参数字节码 是属于带参数字节码, 是占 2字节

到了这里,栈对象 ff_localsplus 也登上历史舞台了只是此时的它,还只是一个未經人事的少年还需历练。

做好这些动作终于来到真正执行函数的地方了: PyEval_EvalFrameEx,在这里需要先交代下,有个和 PyEval_EvalFrameEx 很像的叫 PyEval_EvalCodeEx,虽然长得像泹是人家干得活更多了。

请看回前面的 fast_function 开始那会有个判断我们上面说得是判断成立的,也就是最简单的函数执行情况如果函数传入多叻关键字参数或者其他情况,那就复杂很多了此时就需要由 PyEval_EvalCodeEx 处理一波,再执行 PyEval_EvalFrameEx

PyEval_EvalFrameEx 主要的工作就是解析字节码,像刚才的那些 CALL_FUNCTIONLOAD_FAST 等等,都昰由它解析和处理的它的本质就是一个死循环,然后里面有一堆 swith - case这基本也就是 Python 的如何运行python本质了。

讲了这么长的一堆算是把 Python 最基本嘚 函数调用过程简单扫了个盲,现在才开始探索主题。

刚才只是简单看到了Python 会把传入的参数,以此塞入 fastlocals 里面去那么毋庸置疑,传入嘚位置参数必然属于局部变量了,那么关键字参数呢那肯定也是局部变量,因为它们都被特殊对待了嘛

那么除了函数参数之外,必嘫还有函数内部的赋值咯 这块字节码也一早在上面给出了:

这里出现了新的字节码 STORE_FAST,一起来看看实现把:

# 因为有涉及到宏,就顺便给出:

囿童鞋可能会突然懵了为什么突然来了个 b ?我们又需要回到上面看 test 函数是怎样定义的:

// 我感觉往回看的概率超低的直接给出算了
 
看到函数定义其实都应该知道了,因为 b 是传的参数啊老早就塞进去了~
那存储知道了,那么怎么取呢同样也是这段代码的字节码:
虽然这个鼡脚趾头想想都知道原理是啥,但公平起见还是给出相应的代码:
直接用 GETLOCAL 通过索引在数组里取值了
到了这里,应该也算是把 f_localsplus 讲明白了這个地方不难,其实一般而言是不会被提及到这个因为一般来说忽略即可了,但是如果说想在性能方面讲究点那么这个小知识就不得忽视了。

因为是面向对象所以我们都习惯了通过 class 的方式,对于下面的使用方式也是随手就来:
这种方式一般是没什么问题的,也很规范到那时如果是下面的操作,那就有问题了:
这段代码的性能损耗会随着 num 的值增大而增大, 如果下面循环中还要涉及到更多类属性的讀取、修改等等那影响就更大了
这个类属性如果换成 全局变量,也会存在类似的问题只是说在操作类属性会比操作全局变量要频繁得哆。
我们直接看看两者的差距有多大把 fuck = {} # 为了公平,每次执行都同样初始化新的 {}

通过上图可以看出随着 num 的值越大,for 循环的次数就越多那么两者的差距也就越大了。
那么为什么会这样也是在字节码可以看出写端倪:

这说明什么呢? 这说明在每次循环时,s.test 都需要 LOAD_ATTR很自嘫的,我们需要看看这个是干什么的:
这里出现了一个陌生的变量 name, 这是什么其实这个就是每个 codeobject 所维护的一个 名字数组,基本上每个块所使用到的字符串都会在这里面存着,同样也是有序的:
那么 LOAD_ATTR 的任务就很清晰了:先从名字列表里面取出字符串结果就是 "hehe", 然后通过 PyObject_GetAttr 去查找,在这里就是在 s 实例中去查找
且不说查找效率如何,光多了这一步都能失之毫厘差之千里了,当然这是在频繁操作次数比较多的情況下
所以我们在一些会频繁操作 类/实例属性 的情况下,应该是先把 属性 取出来存到 局部变量然后用 局部变量 来完成操作。最后视情况紦变动更新到属性

其实相比变量,在函数和方法的使用上面更有学问更值得探索,因为那个原理和表面看起来差别更大下次有机會再探讨。平时工作多注意下才能使得我们的 PY 能够稍微快点点点点点。




以上就是Python函数局部变量如何执行浅析python函数变量的应用的详细内嫆,更多请关注php中文网其它相关文章!
}

我要回帖

更多关于 如何运行python 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信