【Python进阶】2.Python整洁之道

· 最近开始用Python做一些工作,发现Python真的是非常友好的一门语言,虽然运行效率比不上C和C++这种语言,但是开发效率真实高的不行,代码量少,学习曲线平滑。虽然有这么多的优点,但是想玩得更溜还是不容易的,那就需要深入了解它语言特性背后的原因,所以看上了这本《深入理解Python特性》,准备近期把它啃完,并把学习笔记也分享出来,本文就是第一篇。
· 由于用的是python2的环境,所以有些python3特有的内容就会略过,请各位见谅。

2.Python整洁之道

2.1 assert断言

· assert断言语句是一种调试工具,用来测试某个断言条件。如果为真就正常执行,如果为假则引发 AssertionError异常,并显示相关错误。
· 断言是为了告诉开发人员程序中发生了不可恢复的错误。对于可以预料的错误,用户可以予以纠正,这时不要使用断言。
· 断言用于程序内部自检,如声明了一些代码中不可能出现的条件。如果触发了条件,则说明程序存在BUG。
· 断言是一种调试辅助功能,不是用来处理运行时错误的机制。用断言的目的是让开发人员更快速的找到可能导致BUG的根本原因。

def apply_discount(product, discount):
	price = int(product['price'] * (1.0 - discount))
	assert 0 <= price <= product['price']
	return price

本函数中的assert为了保证折扣永远不会低于0。

(1)断言语法

assert_stmt ::=  "assert" expression1 ["," expression2]

if __debug__:
    if not expression1: raise AssertionError(expression2)

也就是说
用法1:只有一个参数表达书

assert 0 <= price

用法2:增加了断言提示,可以同步显示

assert False, ("asert note")

(2)注意事项:

  • 不要使用断言来验证数据;

这个时候要用if来判断

  • 永不失败的断言

    非空元组总为真值。比如下面的的语句总是不会产生断言。

assert(1 == 2, 'This should fail')

2.2 巧妙放置逗号

比如有如下列表

names = ['Alice', 'Bob', 'Dilbert']

如果修改了一点点,通过git diff不太容易看出差异,
改变为

names = [
	'Alice',
	'Bob',
	'Dilbert'
	]

但是这样,如果少一个逗号,则不容易看出来,所以改成如下形式,都有逗号,非常整齐:

names = [
	'Alice',
	'Bob',
	'Dilbert',
	]

2.3 with语句

(1)基本特性
· with是一个非常有效的特性,有助于编写更为清晰易读的Python代码。
比如:

with open('hello.txt','w') as f:
	f.write('hello,world!')

· 打开文件一版建议使用with,这样执行完with语句之后会自动关闭这个文件。
· 以上代码可以住转化为:

f = open('hello.txt','w')
try:
	f.write('hello,world!')
finally:
	f.close()

· 这里的try finally语句很重要,保证了当f.write失败时,也会将文件资源释放。
· 而with明显简化了代码,5行变2行。
· 许多资源都可以使用with方法,比如锁。

(2)自定义对象中支持with

class ManagedFile:
	def __init__(self, name):
		self.name = name;
	
	def __enter__(self):
		self.file = open(self.name, 'w');
	
	def __exit__(self, exc_type, exc_val, exc_tb):
		if self.file:
			self.file.close()

with ManagedFile("hello.txt") as f:
	f.write('hello,world!')
	f.write('bye,now!')

· 当执行流程进入with语句上下文时,python会调用__enter__获取资源,当离开with上下文时,会调用__exit__释放资源。

· 还有一种基于contextlib模块的装饰器contextmanager可以简化实现。等后续看到装饰器这块内容再来补充。

2.4 变量命名与下划线

(1)前置单下划线_var:命令约定,用来表示该名称仅在内部使用。
(2)后置单下划线var_:命名约定,用于避免与Python关键字发生命名冲突。
(3)前置双下划线__var:在类环境中使用时会触发名称改写,对Python解释器有特殊含义。
(4)前后双下划线__var__:表示由Python语言定义的特殊方法,自定义变量要避免使用。
(5)单下划线_:有时表示临时或者无意义的变量名称,表示不关心。
总结:1,2项可以自己定义使用,3和5可以在特殊场景使用,4不要使用。

2.5 字符串格式化

(1)"旧式"字符串格式化(python2.7可用, 不推荐使用)
和C语言中printf用法相似:

errno = 987654321
name = 'Bob'
print 'errno=%d,name=%s' % (errno,name)
>>> errno=987654321,name=Bob

· 这种风格的好处就是可以进行自动格式的转换,比如%x用来做16进制显示。

(2)"新式"字符串格式化(python2.7可用)
· 新式格式使用{}来替代旧格式中的字符位置,打印和上个例子一样的做法

print 'errno={},name={}'.format(errno,name)

如果想转换成16进制,就使用:x

print 'errno={:x},name={}'.format(errno,name)

还可以使用别名

print 'errno={e:x},name={n}'.format(e=errno,n=name)

(3)字符串字面值插值(Python3.6+)

(4)模板字符串
此功能并不强大,但是有时候是你所需要的。
但是需要使用内置字符串模块中的Template类,变量则使用类似shell使用的$var方式。

from string import Template
t = Template('Hey,$name')
print t.substitute(name=name)
>>>Hey,Bob

但是模板字符串不能使用格式说明符,因此需要手动转换成16进制字符串:

templ_string = 'errno=$errno,name=$name'
print Template(templ_string).substitute(errno=hex(errno),name=name)
>>>errno=0x3ade68b1,name=Bob

其实模板字符串非常简单,用起来并不方便,但是因为简单,所以安全,可以用在处理用户提供的字符串。
攻击模式演示:

SECRET = 'this-is-a-secret'
class Error:
	def __init__(self):
		pass

err = Error()
user_input = '{error.__init__.__globals__[SECRET]}'
print user_input.format(error=err)
>>>this-is-a-secret

不明白的可以把这个值打印出来,__globals__这个里面是所有的符号表

print err.__init__.__globals__

也就是通过这种方式泄露了全局变量的值,及其危险,换成模板字符串之后,这字符串谁都不认得,所以安全!

上一篇:C语言中的错误处理


下一篇:socket