改善python程序的91个建议读书笔记(5)

建议13:尽量转换为浮点类型后再做除法

python在最初的设计过程中借鉴了c语言的一些标准,比如选择C的long类型作为Python的整数类型.double作为浮点类型.同时标准的算数运算,包括除法,返回值总是和操作符类型相同.python2中建议涉及除法运算的时候,尽量先把操作数转化成浮点类型再做运算.
python3不存在这样的问题.
而且如果是浮点数的比较,一定要指明精度.

建议14:警惕eval()的安全漏洞

eval可以将任何字符串当做表达式求值.会执行危险的命令.

建议15:使用enumerate()获取序列迭代的索引和值.

li = [‘a’, ‘b’, ‘c’, ‘d’, ‘e’]
for i,e in enumerate(li):
print “index:”,i,”element:”,e

enumrate(sequence,start=0)

enumerate()函数的内部实现非常简单.实际相当于如下代码:
def enumerate(sequence, start = 0):
n = start
for elem in sequence:
yield n ,elem
n += 1

建议16:分清== 与is的使用场景

is表示的是对象标识符,而==表示的意思是相等(equal)
is的作用是用来检查对象的标识符是否一致,也就是比较两个对象在
内存中是否拥有同一块内存空间,它并不适合用来判断两个字符串是否相等.

x is y 仅当x和y是同一个对象的时候才返回True,x is b 基本相当于id(x)==id(y)
而== 才是用来检验两个对象的值是否相等,它实际调用内部__eq__()方法,因此
a == b 相当于a.__eq__(b).所以 == 操作符是可以被重载的,而is不能被重载.

建议17:考虑兼容性,尽可能使用Unicode

Python内建的字符串有两个类型:str 和 Unicode.
它们拥有共同的祖先basestring.

unicode是一个编码系统,unicode转换格式称为utf,其中较为常见的为UTF-8.UTF-8的特点是对不同范围的字符串使用不同长度的编码.

Unicode也被称为是万国码,Unicode为每种语言设置了唯一的二进制编码表示方式,提供了从数字代码到不同语言字符集之间的映射,从而可以满足跨平台,跨语言.

decode 是解码, encode是编码 我们要用decode解码成unicode,再encode为其他的类型.
举例:
读入的文件test.txt用UTF-8编码形式保存,但是windows的本地默认编码是CP936,在Windows系统中它被映射为GBK编码,所以当在控制台桑直接显示UTF-8字符的时候不兼容.这个时候就是使用Unicode作为中间介质来完成转换.首先需要对读入的字符用UTF-8进行解码,然后用GBK进行编码,修改之后的结果如下:

filehandle = open("test.txt",'r')
print (filehandle.read().decode("utf-8")).encode("gbk")
filehandle.close()

实例1.读出文件的内容显示为乱码

filehandler = open("test.txt","r")
print filehandle.read()
filehandle.close()

实例2:当python源文件中包含中文字符的时候抛出SyntaxError异常.
unicodetest.py文件中的内容如下:
s = “python 中文测试”
print s
上述程序运行时报错如下:
File “unicodetest.py”, line 1
SyntaxError:Non-ASCII character ‘\xd6’ in file unicodetest.py on line 1, but no encoding declared; see http://www.python.org/peps/pep-0263.html for details

实例3:普通字符和Unicode进行字符串连接的时候抛出UnicodeDecodeError异常

#coding=utf-8
s = "中文测试" + u"Chinese Test"
print s

程序运行抛出异常如下:
Traceback(most recent all last):
File “test.py”, line 2, in<module>
s = “中文测试” + u”Chinese Test”
UnicodeDecodeError: ‘ascii’ codec can’t decode byte 0xd6 in position 0: ordinal not in range(128)

示例1 分析:

错误处理参数有一下三种常用方法:
1) strict:默认处理方式,编码错误抛出UnicodeError异常
2) ignore 忽略不可转换字符
3) replace 将不可转换字符用?替代
A 经过1.decode(A)到 unicode 然后经过 2.encode(B) 到 B B 经过4.decode(B)到 unicode 然后经过 3.encode(A) 到 A

从A到B对应的代码为:
(filehandle.read().decode(“utf-8”)).encode(“gbk”)
提醒:上面的例子在某些情况下(如test.txt是用Notepad软件以utf-8编码形式保存)可能还会出现如下异常:
print (filehandle.read().decode(“utf-8”)).encode(“gbk”)

UnicodeEncodeError:’gbk’ codec can’t encode character u’\ufeff’ in position 0:
illegal multibyte sequence

这是因为有些软件在保存UTF-8编码的时候,会在文件最开始的地方插入不可见的字符BOM(OxEF OxBB OxBF,即BOM),这些不可见字符无法被正确的解析,而利用codecs模块可以方便地处理这种问题.

import codecs
content = open(“test.txt”,”r”).read()
filehandle.close()
if content[:3] == codecs.BOM_UTF8: # 如果存在BOM字符则去掉
content = content[3:]
print content.decode(“utf-8”)

关于BOM:
Unicode存储有字节序的问题,例如”汉”字的Unicode编码是OX6C49,如果将6C写到前面,则为big endian ,将49写在前面则成了little endian. UTF-16以两个字节为编码单元,在字符的传送过程中,为了表明字节的顺序,Unicode规范中推荐使用BOM(Byte Order Mark):即在UCS编码中用一个叫做ZERO WIDTH NO-BREAK SPACE的字符,它的编码是FEFF(该编码在UCS中不存在对应的字符),UCS规范建议在传输字节流千,先传输字符ZERO WIDTH NO-BREAK SPACE.如果接受者收到了FEFF,就表明这个字节流是Big-Endian的,如果收到FFFE,就表明这个字节流是Little-Endian的.UTF-8使用字节来编码,一般不需要BOM来表明字节顺序,但可以用BOM来表明编码方式.字符ZERO WIDTH NO-BREAK SPACE的utf-8编码是EF BB BF.所以如果接受者收到EF BB BF开头的字节流,就知道这是UTF-8编码了.

示例二分析:

Python中默认的编码是ASCII编码(这点可以通过sys.getdefaultencoding()来验证),所以unicodetest.py文件是以ASCII形式保存的,s是包含中文字符的普通字符串.当调用print方法输出的时候会隐式地进行从ASCII到系统默认编码(Windows上为CP936)的转换,中文字符并不是ASCII字符,而此时源文件中又未指定其他的编码方式,Python解释器不知道如何正确处理这种情况,便会抛出异常:SyntaxError:Non-ASCII character ‘\xd6’ in file unicodetest.py on line 1. 因此,要避免这种错误需要在源文件中进行编码声明,声明可以用正则.

“coding[:=]\s*(-\w.]+)”表示不知道如何正确处理这种情况,便会抛出异常:SyntaxError:Non-ASCII character ‘\xd6’ in file unicodetest.py on line 1. 因此,要避免这种错误需要在源文件中进行编码声明,声明可以用正则.

“coding[:=]\s*(-\w.]+)”表示
一般来说进行源文件编码声明有一下三种方式:

1.# coding=<encoding name>
2. !/usr/bin/python
    -*- coding:<encoding name> -*-
3.#!/usr/bin/python
 vim: set fileencoding=<encoding name>:

示例2在源文件头中加入编码声明 #coding=utf-8便可解决问题

实例3分析:使用+操作符来进行字符串的连接时, + 左边为中文字符串,类型为str,右边为Unicode字符串,当两种类型的字符串做连接,将str转换成Unicode时使用系统默认的ASCII编码对字符串进行解码,但由于”中文测试”的ASCII编码为\xd6\xd0\xce\xc4\xb2\xe2\xca\xd4,其中的”中”字的编码\xd6对应的值为214.当其值在0-127的时候Unicode和ASCII是兼容的,转换不会有什么问题,但是当其值大于128的时候,ASCII编码便不能正确的处理这种情况,因而抛出UnicodeDecodeError异常,解决上面的问题有一下两种思路:

# coding=utf-8
s = "中文测试".decode("gbk") + u"Chinese Test"

2) 将Unicode 字符串进行utf-8编码
s = "中文测试" + u"Chinese Test".encode("utf-8")

对于中文字符,为了做到不同系统之间的兼容,建议直接使用Unicode表示方式,Python2.6之后可以通过import unicode_literals自动将定义的普通字符识别为Unicode字符串,这样的字符串的行为将和python3保持一致.

from __future__ import unicode_literals
s = "中文测试"
s
u'\u4e2d\u6587\u6dd4b\u8bd5'
建议18:构建合理的包层次管理module

本质是每个Python文件都是一个模块,使用模块可以增强代码的可维护性和可重用性.
显然在大的项目中将所有的Python文件放到一个目录下并不是一个值得推荐的做法.
这个时候就是包(Package)发挥功效的地方了.

什么是包?简单说包既是目录,但与普通目录不同,他除了包含常规的Python文件(也就是模块)以外,还包含一个__init__.py文件,同时它允许嵌套.包结构如下:
Package/ __init__.py
Module1.py
Module2.py
Subpackage/ __init__.py
Module1.py
Module2.py
包中的模块可以通过”.”访问符进行访问,即”包名.模块名”如上述嵌套结构中访问Package目录下的Module1可以使用Package.Module1,而访问Subpackage中的Module1则可以使用Package.Subpackage.Module1.包中的模块同样可以被导入其他模块中.有以下几种导入方式:
1.直接导入一个包,具体如下:
import Package

2.导入子模块或子包,包嵌套的情况下可以进行嵌套导入,具体如下:
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文件为空时,需要使用完整的路径来申明import语句:
from Package.Module1 import Test

但如果在__init__.py文件中添加from Module1 import Test语句,则可以直接使用from Package import Test 来导入类Test.需要注意的是,如果__init__.py文件为空,当意图使用from Package import * 将包Package中所有的模块导入当前名字空间时并不能使得导入的模块生效,这是因为不同平台间的文件的命令规则不同,Python解释器并不能正确判定模块在对应的平台该如何导入,因此它仅仅执行__init__.py文件,如果要控制模块的导入,则需要对__init__.py文件做修改.

__init__.py文件还有一个作用就是通过在该文件中定义__all__变量,控制需要导入的子包或者模块.在上例的Package目录下的__init__.py文件中添加:

__all__ = [‘Module1’,’Module2’,’Subpackage’]
之后再运行from Package import *,可以看到all变量中定义的模块和包被导入当前名字空间.

from Package import *
dir()

['Module1','Module2','Subpackage','__builtins__','__doc__','__name__','__package__']

包的使用能够带来一下便利:
合理组织代码,便于维护和使用.通过将关系密切的模块组织成一个包,使项目结构更为完善和合理,从而增加代码的可维护性和实用性.一下是一个可供参考的Python项目结构:

能够有效地避免名称空间冲突.使用from Package import Module2 可以将Module2导入当前局部名字空间,访问的时候不再需要加入包名.
举例:

>>> from Package import Module2
>>> Module2.Hi()
Hi from Package Module1

如果模块包含的属性和方法存在同名冲突,使用import module可以有效地避免名称冲突,在嵌套的包结构中,每一个模块都以其所在的完整路径作为其前缀,因此,即使名称一样,但由于模块所对应的其前缀不同,因此不会产生冲突.

>>> import Package.Module2
>>> Package.Module2.Hi()
Hi from Package Module1

>>> import Package.Subpackage.Module2
>>> Package.Subpackage.Module2.Hi()
Hi from Subpackage Module2

第三章 基础语法

建议19:有节制的使用from…import 语句

在使用import的时候注意一下几点:
一般情况下尽量优先使用import a 形式,如访问B时需要使用a.B的形式
有节制的使用from a import B形式,可以直接访问B.
尽量避免使用from a import *,因为这会污染命令空间,并且无法清晰地表示导入了哪些对象.

Python在初始化运行环境的时候会预先加载一批内键模块到内存中,这些模块的相关的信息被存放在sys.modules中,读者导入sys模块后在Python解释器中输入sys.modules.items()便可显示所有预加载模块的相关信息.当加载一个模块的时候,解释器实际上要完成一下动作:
1) 在sys.modules中进行搜索看看该模块是否已经存在,如果存在,则将其导入到当前的局部命名空间,加载结束.
2) 如果在sys.modules中找不到对应模块的名称,则为需要导入的模块创建一个字典对象,并将该对象信息插入sys.modules中.
3)加载前确认是否需要对模块对应的文件进行编译,如果需要则先进行编译.
4)执行动态加载,在当前模块的命名空间中执行编译后的字节码,并将其中所有的对象放入模块对应的字典中.

建议20:优先使用absolute import 来导入模块
if __name__ == "__main__" and __package__ is None:
    import sys
    import os.path
    sys.path[0] = os.path.abspath("./../../")
    print sys.path[0]
    import app.sub1
    __package__ = str("app.sub1")
    from . import string

相比absoulte import ,relative import 在实际应用中反馈的问题较多,因此推荐优先使用absolute import.absolute import 可读性和出现问题后的可跟踪性都更好.当项目的包层次结构较为复杂的时候,显示relative import 也是可以接受的,由于命名冲突的原因以及语义模糊的原因,不推荐使用隐式的relative import ,并且它已经在python3中被移除.

建议21: i+=1 不等于 ++i

你需要明白++i在python总语法是合法的,但是并不是我们理解的通常意义上的自增操作.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
提供的源码资源涵盖了安卓应用、小程序Python应用和Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值