Python 杂项

2017-06-05 language python

在此记录与 Python 相关的乱七八糟的东西。

神奇的 property

在 Python 中绑定属性时,如果直接把属性暴露出去,虽然写起来很简单,但是没办法检查参数;通过 property 即可以将一个函数作为属性,并在函数中对参数的值进行检查。

例如,一个学生的成绩必须在 [0, 100] 这个区间才可以,那么我们就可以使用 property 。

#!/usr/bin/env python
#-*- coding:utf-8 -*-
class Student(object):
    @property
    def score(self):
        return self._score
    @score.setter
    def score(self, value):
        if not isinstance(value, int):
            raise ValueError('score must be an integer!')
        if value < 0 or value > 100:
            raise ValueError('score must between 0 ~ 100!')
        self._score = value

s = Student()
s.score = 60       # OK
print s.score      # OK
s.score = 9999     # Fail

如上,只需要函数添加 @property 修饰符即可;此时,又会创建另外一个修饰符 @score.setter,用于把一个 setter 方法变成属性赋值;如果没有使用 setter,那么该属性为只读。

当然,也可以通过如下方式设置,两者的作用相同。

class Student(object):
    def get_score(self):
        return self._score
    def set_score(self, value):
        if not isinstance(value, int):
            raise ValueError('score must be an integer!')
        if value < 0 or value > 100:
            raise ValueError('score must between 0 ~ 100!')
        self._score = value
    score = property(get_score, set_score)

循环缓冲区

Python 中可以很简单的实现一个固定长度的缓冲区,也就是固定长度的 FIFO 队列。

class RingBuffer:
    def __init__(self, size):
        self.data = [None for i in xrange(size)]

    def append(self, x):
        self.data.pop(0)
        self.data.append(x)

    def get(self):
        return self.data

可以通过如下方式使用。

buf = RingBuffer(4)
for i in xrange(10):
    buf.append(i)
    print buf.get()

## OUTPUT:
#  [None, None, None, 0]
#  [None, None, 0, 1]
#  [None, 0, 1, 2]
#  [0, 1, 2, 3]
#  [1, 2, 3, 4]
#  [2, 3, 4, 5]
#  [3, 4, 5, 6]
#  [4, 5, 6, 7]
#  [5, 6, 7, 8]
#  [6, 7, 8, 9]

另外,实际上可以通过 pop()insert(0, x) 将缓冲区正向输入,如上述的顺序刚好相反。

二进制文件操作

对于二进制文件,可以使用 struct 模块中的 pack、unpack 函数进行读写操作;当然对于单个 Byte 可以通过 ord()、hex() 等函数执行。

#!/usr/bin/env python
#-*- coding:utf-8 -*-
import struct

file = open(r"/tmp/test.bin", "wb")
file.write(struct.pack("idh", 12345, 67.89, 15))
file.close()

# reopen it.
file = open(r"/tmp/test.bin", "rb")
(a, b, c) = struct.unpack("idh", file.read(8+8+2))
print a, b, c

file.seek(0)
b = file.read(1)
print hex(ord(b))   # 先通过ord()将一个字节转成一个数,然后转成16进制的字符串
file.close()

通过 struct 模块写入时,保存的时 struct 转换后的格式;如果要原生写入,可以通过如下的方式写入。

#!/usr/bin/env python
#-*- coding:utf-8 -*-

file = open("test.bin","wb")
file.write("\x5F\x9D\x3E" + ("%c%c" % (0x45, 0xF3)))     # 直接写入

import binascii
hexs = "5B7F"
file.write(binascii.a2b_hex(hexs))

file.close()

其中 struct 模块是基于 C 语言编写的库,可以参考官方文档 struct — Interpret strings as packed binary data

其它

计算常见HASH值

import sys
import hashlib

# BUF_SIZE is totally arbitrary, change for your app!
BUF_SIZE = 65536 # lets read stuff in 64kb chunks!

md5 = hashlib.md5()
sha1 = hashlib.sha1()
sha256 = hashlib.sha256()

with open(sys.argv[1], 'rb') as f:
	while True:
		data = f.read(BUF_SIZE)
		if not data:
			break
		md5.update(data)
		sha1.update(data)
		sha256.update(data)

print("MD5: {0}".format(md5.hexdigest()))
print("SHA1: {0}".format(sha1.hexdigest()))
print("SHA256: {0}".format(sha256.hexdigest()))

首行

可以使用 #!/usr/bin/python 或者 #!/usr/bin/env python ,env 可以通过指定的环境变量执行命令。

使用 UTF-8

对于非英文的编码可以使用如下的方法之一。

#-*- coding:utf-8 -*-
#xxx coding:utf-8 xxx
#encoding: utf-8
#coding: utf-8

带 * 参数

在 python 函数参数中,星号的作用是:*)传入任意长度的元组;**)传入任意长度的字典。如果两者之前有固定的参数列表,则先填充前面的参数。

#!/usr/bin/env python
def one_stars(x, *args):
    print 'one_stars, %s:%s' % (type(args), args)
def two_stars(x, **args):
    print 'two_stars, %s:%s' % (type(args), args)
def mix_stars(x, *cargs, **kargs):
    print 'mix_stars, %s:%s, %s:%s' % (type(cargs), cargs, type(kargs), kargs)

one_stars(1, 'abc', 123)
two_stars(1, a=1, b=2)
mix_stars(1, 1, 2, a=1, b=2) # (1, a=1, b=2) OK, (1, a=1, b=2, 1, 2) ERROR
mix_stars(x=1, b=2)

# OUTPUT:
# one_stars, <type 'tuple'>:('abc', 123)
# two_stars, <type 'dict'>:{'a': 1, 'b': 2}
# mix_stars, <type 'tuple'>:(1, 2), <type 'dict'>:{'a': 1, 'b': 2}
# mix_stars, <type 'tuple'>:(), <type 'dict'>:{'b': 2}

列表推导

列表推导的格式为 [ expression for variables in list [if condition]],推导的变量可以有一个或者多个,如果是多个通常为笛卡尔乘积。

print [x if x%2==0 else -x for x in range(1, 10)]
print [x*y for x in [1,2,3] for y in [1,2,3]]
print [(x, y) for x in range(10) if x % 2 if x > 3 for y in range(10) if y > 7 if y != 8]
print [line.rstrip() for line in open('test.txt') if line[0]=='n']
print dict([(x,x*10) for x in range(3)])

print {x:x % 2 == 0 for x in range(1, 11)}     # Python3

三元运算

在 Python 中 0''[](){}None 都为假,而且对于 and 如果所有值为真则会返回最后一个值,否则返回第一个假值;对于 or 如果有一个值为真则返回,否则返回最后一个值。

'' and 'b'             # 返回 ''
'a' and 'b' and 'c'    # 返回 'c'
'a' or 'b'             # 返回 'a'
'' or [] or {}         # 返回 {}

对于三元运算,如取最小值 small = x if x < y else y。也可以使用 and-or ,如 (1 and [a] or [b])[0],使用列表是为了防止 a 为假。

join()格式输出

生成一串数字,中间用逗号分割,其中 range 可以用 xrange 替换。

print ','.join([str(i) for i in range(1,6)])
print ','.join(["%s"%i for i in range(1,6)])
print ",".join(map(lambda i:str(i), range(1,6)))

None 判断

通常的判断方式可能会有多种,不过可能在使用时有很多的问题,列举如下。

if x is None / x==None

(ob1 is ob2) 等价于 (id(ob1) == id(ob2)),也即当比较相同的对象实例,is 总是返回 True,而 == 最终取决于 eq()

class foo(object):
   def __eq__(self, other):
      return True
f = foo()
f == None             # True
f is None             # False

list1 = [1, 2, 3]
list2 = [1, 2, 3]
list1==list2          # True
list1 is list2        # False

if not x

需要注意此时无法区分 x==[]x==None,在 Python 中,NoneFalse、空字符串 ""0、空列表 []、空字典 {}、空元组 () 都相当于 False

if not x is None

实际为 if not (x is None),最好使用 if x is not None

os._exit()/sys.exit()

Python 程序有两中退出方式:

  1. os._exit() 会直接将程序终止,之后的所有代码都不会继续执行;
  2. sys.exit() 会引发一个 SystemExit 异常,如果这个异常没有被捕获,那么 python 解释器将会退出,如果有捕获此异常的代码,那么这些代码还是会执行。
try:
    sys.exit()
except SystemExit:   # 不包含信息,不要使用 SystemExit, e
    print "die"

内置函数 map()、filter()、reduce()

这三个函数比较类似,都是应用于序列 (list, tuple, str等) 的内置函数。

map(function, sequence[, sequence, …]) return: list

对 sequence 中的 item 依次执行 function(item),执行结果输出为 list。如果长度不同,则以最长为准,最短以 None 补齐。

reduce(function, sequence[, initial]) return: value

对 sequence 中的 item 顺序迭代调用 function,必须要有 2 个参数。如果有第 3 个参数,则表示初始值。需要注意的是,function 不能为 None 。

filter(function or None, sequence) return list, tuple, or string

对 sequence 中的 item 依次执行 function(item),将执行结果为 True 的 item 组成 list、string、tuple,这取决于 sequence 的类型。

以下为部分示例。

>>> def add(m, n): return m+n
>>> map(add, range(5), range(2, 7))                  序列的长度必须相同
>>> map(lambda x,y:x+y, range(5), range(2, 7))
>>> map(lambda x:x+x, range(5))
>>> map(None, [1, 3, 5], [2, 4, 6])                  此时等同于zip函数

>>> reduce(lambda x,y:x*y, (1,2), 5)                 5*1*2

>>> filter(lambda x:x%2,i range(10))

lambda 函数

快速定义单行的最小函数,类似于内联函数,不允许使用if函数。

>>> g = lambda x: x+2
>>> g(3)
>>> (lambda x: x+2)(3)

>>> collapse = True
>>> processFunc = collapse and (lambda s: " ".join(s.split())) or (lambda s: s)
>>> s = "this   is\na\ttest"
>>> print s
>>> print s.split()
>>> print " ".join(s.split())

processFunc 会根据 collapse 输出 “this is a test” 或者"this is\na\ttest" 。

字符串

在 Python 中,字符串不以 NULL 结尾,内部表示时会指定字符串的长度,而且一个字符串中可以有多个 NULL 字符。

前缀 r R

在 Python 的 string 前面加上 r, 是为了告诉编译器这个 string 是个 raw string,不要转义 \ 符号。例如,\n 在 raw string 中,是两个字符,而不会转意为换行符。由于正则表达式和 \ 会有冲突,因此,当一个字符串使用了正则表达式后,最好在前面加上 r

前缀 u U

uU 开头的字符串表示 unicode 字符串。