编写高质量代码--改善python程序的建议(三)
原文发表在我的博客主页,转载请注明出处!
建议十三:警惕eval()的安全漏洞
相信经常处理文本数据的同学对eval()一定是欲罢不能,他的使用非常简单:
eval("1+1==2") #进行判断
eval("'A'+'B'") #字符连接
eval("1+2") #数字相加
python中eval()函数将字符串str当成有效的表达式来求值并返回计算结果,其函数声明如下:
eval(expression[, globals[,locals]])
#globals为字典形式,表示全局命名空间
#locals为任何映射对象,表示局部命名空间
“eval is evil”,这时一句广为人知的对eval的评价,它主要针对的是eval()的安全性。假设web页面调用eval来根据用户的输入,计算python表达式的值,由于网络环境下运行它的用户并非都是可信任的,eval()可以将任何字符串当作表达式求值,这也就意味着有空子可钻,如果用户输入下面代码,就会得到当前目录下的所有文件列表。
__import__("os").system("dir")
如果有人想搞破坏,他输入了如下字符串(禁止尝试),则会删掉当前目录下的所有文件
__import__("os").system("del * /Q")
当然有人说可以在globals参数中禁止全局命名空间的访问,比如将运算范围限定为几个常用的数学函数:
math_fun_list = ['acos', 'asin', 'atan', 'pi']
math_fun_dict = dict([(k, globals().get(k) for k in math_fun_list)])
eval(string, {"__builtins__":None}, math_fun_dict)
这样确实解决了安全问题,但是试试输入以下字符:
[c for c in ().__class__.__bases__[0].__subclasses__() if c.__name__ == 'Quitter'][0](0)()
# ().__class__.__bases__[0].__subclasses__()显示object类的所有子类,类Quitter与“quite”功能绑定,因此上面的输入会直接导致程序退出。
因此在实际应用过程中,如果使用对象不是信任源,应该尽量避免使用eval,在需要使用eval的地方可以用安全性更好的ast.literal_eval替代。
建议十四:使用enumerate()获取序列迭代的索引和值
函数enumerate()主要是为了解决在循环中获取索引以及对应值的问题,它具有一定的惰性,每次仅在需要的时候才会产生一个(index,item)对。使用如下:
enumerate(sequence, start=0)
#sequence可以是list、 set等任何迭代对象
#默认从0开始
#函数返回一个迭代器,可以使用next()方法获取下一个元素
建议十五:分清 == 与 is 的使用场景
is表示的是对象标识符(object identity),即比较两个对象在内存中是否拥有同一块内存空间,而==表示的意思是相等(equal),用来检验两个对象的值是否相等。
还有需要注意的是,在python中有string interning(字符串驻留)机制:对于较小的字符串,为了提高系统性能会保留其值的一个副本,当创建新的字符串的时候直接指向该副本即可。而长字符串不会驻留。
建议十六:考虑兼容性,尽可能使用Unicode
python内建的字符串有两种类型:str和Unicode,它们拥有共同的祖先basestring。
Unicode也称做万国码,它为每种语言设定了唯一的二进制编码表示方式,提供从数字代码到不同语言字符集之间的映射,从而可以满足跨平台、夸语言之间的文本处理要求。
Unicode编码系统可以分为编码方式和实现方式两个层面,在编码方式上,分为UCS-2和UCS-4两种方式,UCS-2用两个字节编码,UCS-4用4个字节编码。一个字符的Unicode编码是确定的,但是在实际传输过程中,由于系统平台的不同以及处于节省空间的目的,实现方式有所差异。Unicode的实现方式称为Unicode转换格式,简称为UTF,包括UTF-7、UTF-16、UTF-32、UTF-8等,较为常见的是UTF-8.他的特点是对不同范围的字符使用不同长度的编码,其中0x00 ~ 0x7F的字符的UTF-8编码与ASCII编码完全相同,UTF-8编码的最大长度是4个字节。
通常用python处理中文字符经常会遇见一下几个问题。
- 读出文件的内容显示为乱码
fp = open("test.txt","r")
print fp.read()
fp.close()
问题:读入的文件test.txt用UTF-8编码形式保存,但是Windows的本地默认编码是CP936,在Windows系统中它被映射为GBK编码,所以直接显示UTF-8字符的时候,不兼容。解决办法:首先对读入的字符用UTF-8进行解码,然后再用GBK进行编码。
fp = open("test.txt","r")
print (fp.read().decode("utf-8")).encode("gbk")
decode()方法讲其他编码对应的字符串解码为Unicode,而encode()方法将Unicode编码转换为另一种编码。
另外:上面的例子在某些情况下(如test.txt使用Notepad软件以UTF-8编码形式保存)可能还会出现异常,这是因为有些软件在保存UTF-8编码的时候,会在文件最开始的地方插入不可见的字符BOM,利用codecs模块可以方便地处理这种问题
import codecs
fp = open("test.txt",'r')
content = fp.read()
fp.close()
if content[:3] == codecs.BOM_UTF8:
content = content[3:]
print content.decode("utf-8")
- 当python源文件中包含中文字符的时候抛出SyntaxError异常
python中默认的编码是ASCII编码,为了让接收器知道如何正确处理字符,需要在源文件中进行编码声明,声明可以用正则表达式表示
"coding[:=]\s*([-\w.]+)"
一般来说进行源文件编码声明有三种方式:
#coding=<encoding name>
#!/usr/bin/python
# -*- coding: <encoding name> -*-
#!/usr/bin/python
# vim: set fileencoding=<encoding name>:
- 普通字符和Unicode进行字符串连接的时候抛出UnicodeDecodeError异常。
# coding=utf-8
s = "中文" + u"Chinese Test"
print s
使用 + 操作符来进行字符串的连接时,左边为中文字符串,类型为str,右边为Unicode字符串,当两种类型的字符串连接的时候,python将左边的中文字符串转换为Unicode再与右边的Unicode字符串连接,将str转换为Unicode时使用系统默认的ASCII编码对字符串进行编码,就会出现UnicodeDecodeError异常。解法办法:
- 指定str转换为Unicode时的编码方式。
#coding=utf-8
s = "中文".decode('gbk') + u"Chinese Test"
- 将Unicode字符串进行UTF-8编码
s = "中文" + u"Chinese Test".encode("utf-8")
建议十七:构建合理的包层次来管理module
本质上每一个python文件都是一个模块,使用模块可以增强代码的可维护性和可重用性。我们需要包(Package)来合理的组织项目的层次来管理模块。
包即目录,但是与普通目录不同,它除了包含常规的python文件(模块)以外,还包含一个__init__.py文件,同时它允许嵌套。包结构如下:
Package/ __init__.py
Module1.py
Molude2.py
Subpackage/ __init__.py
Module1.py
Module2.py
包中的模块可以通过 . 访问符进行访问,即包名.模块名。如上述嵌套结构中访问Package下的Module1可以使用Package.Module1,而访问Subpackage中的Module1则可以使用Package.Subpackage.Module1
包中的模块同样可以被导入其他模块中,有以下几种方法:
- 直接导入一个包
import Package
- 导入子模块或者子包
from Package import Module1
import Package.Module1
from Package import Subpackage
import Package.Subpackage
from Package.Subpackage import Module1
import Package.Subpackage.Module1
前面提到包的目录下应该有init.py文件,它除了区分包和普通目录,还可以在该文件中申明模块级别的import语句从而使其编程包级别可见。举例来看:
上例中如果要import包Package下Module1中的类Test,当__init__.py为空的时候需要使用完整路径
from Package.Module1 import Test
但是如果在__init__.py中添加from Module1 import Test语句,则可以直接使用下面语句来导入类Test
from Package import Test
注意:如果__init__.py为空,当意图使用***from Package import ****将包Package中所有的模块导入当前名字空间时并不可以,这是因为不同平台间的命名规则不同,python解释器不能正确判定模块在对应平台如何导入,因此它仅仅执行__init__.py文件,如果要控制模块的导入,则需要修改__init__.py
__all__ = ['Module1', 'Module2', 'Subpackage']
这样就可以了。
包的使用可以带来如下便利:
- 合理组织代码,易于维护和使用。以下是一个可供参考的python项目结构:
ProjectName/
|---README
|----LICENSE
|----setup.py
|-----requirements.txt
|------sample/
| |----__init__.py
| |----core.py
| |----helpers.py
|------docs/
| |------conf.py
| |------index.rst
|------bin/
|------package/
| |-----__init__.py
| |-----subpackage/
| |-----......
|------tests/
| |------test_basic.py
| |------test_advancde.py
- 能够有效地避免名称空间冲突。
总结:至此罗列了python一些惯用法,掌握和熟练使用这些是非常必要的,后面会接着说一些基础语法需要注意的地方。
参考:编写高质量代码--改善python程序的91个建议
编写高质量代码--改善python程序的建议(三)的更多相关文章
- 编写高质量代码--改善python程序的建议(六)
原文发表在我的博客主页,转载请注明出处! 建议二十八:区别对待可变对象和不可变对象 python中一切皆对象,每一个对象都有一个唯一的标识符(id()).类型(type())以及值,对象根据其值能否修 ...
- 编写高质量代码--改善python程序的建议(八)
原文发表在我的博客主页,转载请注明出处! 建议四十一:一般情况下使用ElementTree解析XML python中解析XML文件最广为人知的两个模块是xml.dom.minidom和xml.sax, ...
- 编写高质量代码--改善python程序的建议(七)
原文发表在我的博客主页,转载请注明出处! 建议三十四:掌握字符串的基本用法 编程有两件事,一件是处理数值,另一件是处理字符串,在商业应用编程来说,处理字符串的代码超过八成,所以需要重点掌握. 首先有个 ...
- 编写高质量代码–改善python程序的建议(五)
原文发表在我的博客主页,转载请注明出处! 建议二十三:遵循异常处理的几点基本原则 python中常用的异常处理语法是try.except.else.finally,它们可以有多种组合,语法形式如下: ...
- 编写高质量代码--改善python程序的建议(四)
原文发表在我的博客主页,转载请注明出处! 建议十八:有节制的使用from...import语句 python提供了三种方式引入外部模块: import语句 from...import... __imp ...
- 编写高质量代码–改善python程序的建议(二)
原文发表在我的博客主页,转载请注明出处! 建议七:利用assert语句来发现问题断言(assert)在很多语言中都存在,它主要为调试程序服务,能够快速方便地检查程序的异常或者发现不恰当的输入等,可防止 ...
- 编写高质量代码--改善python程序的建议(一)
原文发表在我的博客主页,转载请注明出处! 初衷 python是一个入门十分容易的编程语言,但是想要写好python却是一件不容易的事情,如果不是专业使用python的人,只是将python作为一个脚本 ...
- 编写高质量代码改善python程序91个建议学习01
编写高质量代码改善python程序91个建议学习 第一章 建议1:理解pythonic的相关概念 狭隘的理解:它是高级动态的脚本编程语言,拥有很多强大的库,是解释从上往下执行的 特点: 美胜丑,显胜隐 ...
- 编写高质量代码 改善Python程序的91个建议 (读后 小记)
此书是自己好久之前买的,当时总觉得Python语言中有各种trick, 总是要自己猝不及防的掉入到陷阱之中, 看了一些资料后发现了这本书,感觉很是不错,不过可惜自己平时总是杂事太多,总是找不到整块的时 ...
随机推荐
- C++实现对数学基本运算表达式的解析
代码地址如下:http://www.demodashi.com/demo/11078.html 前段时间在LeetCode上刷题,遇到了很多涉及对字符串进行解析的题目.可能是出于这个原因,最近迷恋上了 ...
- C#实现播放声音的方法
文章来自学IT网:http://www.xueit.com/html/2009-09/21_4598_00.html 在这里介绍使用C#实现播放声音的几种方法,都是利用组件等方法来实现的,有兴趣的话可 ...
- android 屏幕适配原则
屏幕大小 1.不同的layout Android手 机屏幕大小不一,有480x320,640x360,800x480.怎样才能让App自动适应不同的屏幕呢? 其实很简单,只需要在res目录下创建不同的 ...
- Machine-wide Progress Telerik Fiddler installation has been found at ...Please, use that one or uninstall it ...
问题描述:无法安装Fiddle,提示已有Fiddle,但是却找不到. 打开Fiddle,提示“Machine-wide Progress Telerik Fiddler installation ha ...
- Office 365 离线安装
原创作品,允许转载,转载时请务必以超链接形式标明文章 原始出处 .作者信息和本声明.否则将追究法律责任.http://yueque.blog.51cto.com/4580340/1707479 有个O ...
- WCF实现客户端和服务端
service side 1.定义ServiceContract: 2.new a ServiceHost 3. add endpoint using System.ServiceModel; nam ...
- 使用PHP创建一个REST API(Create a REST API with PHP)
译者前言: 首先这是一篇国外的英文文章,非常系统.详尽的介绍了如何使用PHP创建REST API,国内这方面的资料非常非常的有限,而且基本没有可操作性.这篇文章写的非常好,只要对PHP稍有了解的程序员 ...
- FPGA开发流程1(详述每一环节的物理含义和实现目标)
要知道,要把一件事情做好,不管是做哪们技术还是办什么手续,明白这个事情的流程非常关键,它决定了这件事情的顺利进行与否.同样,我们学习FPGA开发数字系统这个技术,先撇开使用这个技术的基础编程语言的具体 ...
- 【ZT】NUMA架构的CPU -- 你真的用好了么?
本文从NUMA的介绍引出常见的NUMA使用中的陷阱,继而讨论对于NUMA系统的优化方法和一些值得关注的方向. 文章欢迎转载,但转载时请保留本段文字,并置于文章的顶部 作者:卢钧轶(cenalulu) ...
- opencv读取图像输入到tensorflow模型中进行运算【cpp】
void TransformMatToTensor(const cv::Mat &image, Tensor &input_tensor, int input_width, int i ...