python的一些技术积累

Python

同一段代码在函数运行快于直接运行的问题

问题:
最近覃武提了一个有意思的问题:
“同一段代码”放在函数里运行要比直接书写运行的更快。

for i in xrange(10**8):
    pass

原因:
今天查了资料,发现其实蛮有意思的其本质是
局部变量的操作速度要快于全局变量
python代码运行之前会编译为字节码(.pyc),可以发现两段代码的差异就是局部变量和全局变量的差异,局部变量采用STORE_FAST的方式操作,全局变量只能采用STORE_NAME的方式操作,当然前者速度快于后者.
一个不可理解的问题背后,深究一下,都会涉及很多有意思的问题(实际还会比我这描述的更深)

python的多线程没有效果

问题:
覃武为了翻身,写了一个计算历史双色球出现概率的python程序,由于涉及的计算量很大,他采用了多线程
但是实际发现,多线程只能让一个核满负荷,其他都是闲置的.为什么呢?
原因:
python有GIL锁,多个线程其实本质都是一个一个轮流干活的.
解决:
如果需要榨干多核电脑性能,需要多进程,而不是多线程.
延伸:
根据历史概率真的能计算未来吗?坐等他暴富中…

python的TCP Server无法被非本机电脑访问

问题:
用python开发tcp server用于测试时,很常见的问题就是模仿网上例子,将host设置为”localhost”,结果发现除了本机可以访问,其他电脑都访问不了.
解决:
正确的做法是将host设置为空字符串,这意味着所有的连接都可以访问这个server,这是一个非常常见的错误.

危险和慢的eval函数

python的eval函数是个很万能,但又是不推荐的函数,原因主要是”不安全”,”慢”等.
“不安全”,举例,假如有人故意将数据字典的普通的ID,改为”os.system(‘rm -rf /’)”,在别人不知情的情况下运行,那么整个设备就会瘫痪,这是一个典型而简单的注入式攻击.
“慢”,对于将输入转化为整数的应用,int()比eval()快了20倍,将解析工具中的eval改为安全的int(),整体提速了15%.
为了取代eval,int可以采用int(xx,0),第二个参数为0意味着让int去猜测输入,如果输入是0x开头,自动将其识别为16进制,非常好用.
有一点想不通的是,这种猜测模式居然比普通的int使用还要快,真不知道为什么了.

字典的排序

python的字典(dict)建立后,迭代它,输出的顺序和建立时是不一样的.
如果你的需求只是按一定的顺序输出,采用

sorted(d.items())

就行了,注意,排序后的输出不是字典而是列表了.
如果你需要按建立时的顺序输出,上一个方法是不行的,这时可以采用python提供的其他容器.

collections.OrderedDict()

采用这个顺序容器,可以保证迭代字典时,仍然按建立时的顺序输出,而且它也不需要再变为列表了.

本地py文件建议不要和库文件名字相同

python的程序不应与导入的库的文件同名,否则会产生一些诡异的问题,如导入库错误.
举例,为了测试发邮件功能,自己的代码常会写为email.py,但恰巧smtp库里就有email的同名库文件,当需要导入库的email文件时,会从本地先搜索,就会拿错自己的文件作为库文件,出现一些诡异的问题.换个名字就正常了.

python的编码问题

python2的字符编码问题,是个基本都会碰到的问题。
python2默认采用ascii编码。
如果代码中会出现中文的字符串,可以在文件开头加入
# encoding=utf8
,说明字符串均为utf-8,否则会出错。
在使用python2的xlrd库处理excel时,xlrd中类的内部变量如,sheet名和列名等,都是unicode,取出来后,要encode(‘utf-8’),才方便与代码中的字符串比较和合并等操作。
在unicode和str类型直接操作时(如相加),python会将str自动转为unicode后,再操作生成一个unicode类型的结果。
由于python2默认采用ascii进行codec,当碰到中文时,这个操作就会出错,很讨厌。
这里在文件开头加入三句:

import sys
reload(sys)
sys.setdefaultencoding('utf-8')

意思是,将默认编码变为utf-8,上述操作str就会以utf-8格式decode成unicode,这样就可以直接支持中文了。
同时,encode()和decode()中间不加指定编码时,都是’utf-8’,方便很多。注意:reload(sys)的原因是,python的开发大仙在import sys后,将setdefaultencoding函数去掉…所以只能重新reload一遍才能使用那个函数。 python中字符编码问题,强烈推荐阅读该文加深理解:http://nedbatchelder.com/text/unipain.html

python内存消耗分析

如果python内存消耗比较多,这时需要内存分析工具,这里使用momory_profiler模块。
from memory_profiler import memory_usage
然后print(memory_usage(-1))
就可以在这句的时候打印当前进程的内存占用(-1表示当前进程)。
如果需要详细分析函数内的使用,可以
from memory_profiler import profile
然后目标函数加入装饰器@profile。
就可以在目标函数执行时,输出详细的内存分配使用情况。在一般的内存分析可以看出,python为了执行效率考虑,内存往往只增不减。这时可以
import gc模块。
在需要garbage collection的时候,加入gc.collect()即可明显看到内存减少了。

pypy,python和C的简单性能比较

python2,pypy和C的运行速度差别多大呢,今天做了几个测试:
1.纯cpu计算,进行100万次加减乘除,
成绩为python2:340ms;pypy:26ms;C:12ms,
可以看到标准版python2真是慢的可以,pypy和C的差距较小;
2.IO操作,进行100万次print的输出,python2和pypy都是20.3s,C是13.9s,
可以看到IO操作的瓶颈在于硬件,语言差距不大;
3.sqlite数据库操作,python和C的时间几乎差不多。
可以看到,纯cpu计算,python2是c的28倍时间,pypy是c的2倍时间;纯IO操作,则相差很小,python2和pypy均为C的1至1.5倍时间之间。

如何用字符串映射变量

python中,通过getattr和setattr可以用字符串映射变量名,举例,当输入为“abc”字符串时,要求class里的名为abc的变量进行相应处理,怎么操作呢?
这时,是不能用字典的,举例,字典加入如下条目{“abc”:abc},那么下次dict[“abc”]输出的是当时abc的值,是一个常量,而不是我们要的变量,这点要注意。
因此,这里需要用getattr。在class里执行getattr(self, "abc")就可以得到名为abc的变量了,那么setattr(self, "abc", 1)可以将class中的变量abc设置为值1。

如果是函数,可以这么做:

thismodule = sys.modules[__name__]
setattr(thismodule, "a", 1)

有更简单的方法:

vars()["a"] = 1

去除列表中空字符串最快最简单的方法

推荐采用:

filter(None, your_list)

具有比

while '' in your_list:
    your_list.remove('')
和
your_list = [x for x in your_list if x != '']

更快的速度

filter的使用参考:
https://docs.python.org/3/library/functions.html#filter

注意:
空字符串 会被程序判定为 False
filter(None, your_list), None代表不输入函数,也就是
[x for x in your_list if x]

测试可以采用timeit

timeit.timeit("filter(None, l_ori)", 'l_ori = 10*["a", "b", "", "", "c", "", "d", "e", "f", "", "g"]', number = 1000)

第二个参数,是setup,也就是准备的数据。

猜你喜欢

转载自blog.csdn.net/yyw794/article/details/78114612