python注意点

关于编码

UTF-8编码是费内存的,但是却是安全的,如果是GBK编码时,下面的表达式返回True:

'嵌' in '我们都是中国人'

注意UTF-8编码也有两字节的,而且跟三字节的模样还挺像:

In [1]: '»'
Out[1]: '\xc2\xbb'

In [2]: '》'
Out[2]: '\xe3\x80\x8b'

其他一些有趣的符号:

raquo »laquo «rsaquo ›lsaquo ‹rdquo ”ldquo “rsquo ’lsquo ‘

日元符号:

In [5]: '¥'
Out[5]: '\xc2\xa5'

人民币符号:

In [6]: '¥'
Out[6]: '\xef\xbf\xa5'

关于一些有用的数据结构

Queue是一个线程安全的队列,初始化的maxsize<=0时,表示队列容量无限。Queue是一个线程安全的队列,内部自行加锁。 put和get的时候,要注意Queue.Full和Queue.Empty异常,有趣的是,默认会阻塞,直到put或get成功。

q = Queue.Queue(1024)
q.put(self, item, block=True, timeout=None)
q.get(self, block=True, timeout=None)

要么就设置block=False,这样timeout就被忽略了,相当于put_nowait()或者get_nowait()。要么就设置一个timeout值。

collections.defaultdict跟普通dict一样,只是在没有设置时,会根据初始化的默认工厂函数设置一个默认值。

dd = collections.defaultdict(list)

collections.namedtuple跟普通tuple也一样,只是它是有名字的tuple,更像是C++里的结构体。当传递多个值时, 使用namedtuple比直接使用tuple更优雅:

Student = collections.namedtuple('Student', 'name age school x y')
s = Student('wangyang', 20, 'hust', x=100, y=200)
s.name #像类一样访问成员
s[0]   #像tuple一样用下标访问
list(s)
name,age = s[:2]

关于用iter来省内存

就像xrange和range的差别,前者是每次循环执行一步,而后者是循环一个list,前者更省内存。 有两种方法来实现,其一,实现一个类的__iter__()next()方法,这样每次循环的时候,推进一步:

class Fab(object): 

    def __init__(self, max): 
        self.max = max 
        self.n, self.a, self.b = 0, 0, 1 

    def __iter__(self): 
        return self 

    def next(self): 
        if self.n < self.max: 
            r = self.b 
            self.a, self.b = self.b, self.a + self.b 
            self.n = self.n + 1 
            return r 
        raise StopIteration()

for n in Fab(5):
    print n

其二,就是使用yield,当遇到yield时,循环取一次值,下一次循环时,再次从上次yield处开始执行,函数返回时,表示循环结束:

def Fab(max):
    n, a, b = 0, 0, 1
    while n < max:
        yield b
        a, b = b, a+b
        n += 1

for i in Fab(5):
    print(i)

关于if判断

  1. 当判断是否为None时,使用is Noneis not None==!=会调用__eq__函数。
  2. 当判断是否为True和False时,使用exprnot expr,道理同上。
  3. 当判断是否为0时,使用==!=,语义更清晰。

关于正则使用

python正则模块提供如下一些核心方法:

  1. re.sub是正则替换,返回替换后的结果字符串
  2. re.match是正则匹配,是从一行的开始开始匹配
  3. re.search是正则查找,并不从开始匹配,但只查找一个结果
  4. re.findall正则查找,返回一个list,包含所有查找结果
  5. re.finditer跟findall很像,只是迭代进行,省内存

上面函数的参数大体是pattern, string, flags=0这么三个,pattern可以是正则表达式,也可以是一个编译好的pattern对象, flags是一些bit位或起来,控制正则形为。flags也可以在编译正则的时候给出:

re.compile(r'<div[^>]*content[^>]*>(.*?)[\n ]</div>', re.MULTILINE | re.DOTALL)

re.MULTILINE表示多行匹配,默认是按行匹配的,通常搭配上re.DOTALL,表示点(.)可以匹配包括回车换行在内的任何字符, 否则是不能匹配回车换行的。.*?中的问号表示使用非贪婪算法。默认* + ?都是贪婪的,后面加个问题成*? +? ??就成非贪婪的了。

在没有设置UNICODE标志情况下,有下表常用匹配符:

匹配符意义
\A 匹配字符串开头
\b 匹配一个词的开头或者结尾
\B 匹配一个词的词中,词中字符集等于\w
\d [0-9]
\D [^0-9]
\s [ \t\n\r\f\v]
\S [^ \t\n\r\f\v]
\w [a-zA-Z0-9_]
\W [^a-zA-Z0-9_]
\Z 匹配字符串的结尾

关于参数默认值

函数默认参数使用常量,否则有调用者传入,不然很容易产生非预期结果,代码也很让人费解:

def get_now(time = time.time()):
    print(time)

反复调用上面的函数,输出是不会有变化的。函数的成员函数道理也是一样的。

关于json使用

不加s的版本,都是针对文件的读写:

with open("output.txt", 'a+') as f:
    json.dump(d, f, ensure_ascii=False, indent=4)
    f.write("\n")

indent会多行缩进,优雅打印。加载进来的时候,文件中只能是一个json:

with open('output.txt') as f:
    a = json.load(f)

针对字符串时,应该使用json.dumpsjson.loads。python在加载json时,要求json里使用双引号:

json.loads("{'name':123, 'age':'123'}") #Error
json.loads('{"name":123, "age":"123"}') #OK

而且带引号的"123"会被解析为字符串,而123会被解析为整型,null会被解析为None:

>>> json.loads('{"name":null, "age":"128"}')   
>>> {u'age': u'128', u'name': None}

关于argparse使用

使用argparse,可以很方便地让脚本获取启动时的命令参数,举个例子:

import argparse
parser = argparse.ArgumentParser(description='Process some integers.')
parser.add_argument('--name', dest='name', required=True, help='your name')
parser.add_argument('-a', dest='age', nargs=1, type=int, help='your age')
parser.add_argument('-s', dest='scores', nargs='+', default=[0], help='your english scores')
parser.print_help()
ns = parser.parse_args('--name wangyang -a 100 -s 60 70 80'.split())
print(ns.name, ns.age, ns.scores)

参数非常多,大体为:

  1. dest表示最终解析之后,存储的变量名
  2. required是否必须
  3. nargs对应几个参数,可以为一个整数,或者使用通配符,如?+*
  4. default默认值
  5. type读取时参数类型检查 parser.parse_args()参数为空时,就会使用sys.argv。

关于ConfigParser使用

python读取配置格式比较宽松:

[My Section]
foodir: %(dir)s/whatever
dir=frob
long: this value continues
   in the next line

可以使用冒号(:)或者等号(=),整行注释,可使用井号(#)或者分号(;),行尾注释只能使用分号(;)。

import ConfigParser
config = ConfigParser.SafeConfigParser()
config.read('/tmp/spider.conf')
config.has_section('spider')
url = config.get('spider', 'target_url')
发表于 2014年09月19日 00:24   评论:0   阅读:2220  



回到顶部

首页 | 关于我 | 关于本站 | 站内留言 | rss
python logo   django logo   tornado logo