编程风格
-
分号
- 不要在行尾加分号
- 不要使用分号将两行连在一起
-
行长度
- 行长度不超过80个字符
- 长的URL链接,例外
- 长的模块导入语句,例外
- 不要使用换行符连接两行,可使用'()'隐式连接
-
括号
- 不要在返回语句或条件语句中使用'()'
-
缩进
- 用4个空格代替缩进
- 不要使用tab来缩进
-
空行
- 顶级定义(模块变量、函数、类定义)之间空两行
- 方法定义之间空一行
-
空格
- 括号内不要有空格
- ',',':'前不要有空格
- 参数列表、索引、切片的左括号前不要加空格
- 二元操作符(<, <=, ==, !=, and, not, or, in, is)的两边要加上空格
- '='用户关键字参数时,两边不要加空格
- '='用户赋值语句是,两边要加空格
-
注释(文档字符串)
- 模块--每个文件都应该包含许可样板
- 函数和方法
- Args: 列出每个参数的名字和描述
- Returns: 描述返回值的类型和定义
- Raises: 列出与接口有关的所有异常
- 类
- 类应该在其定义下有一串描述类功能的字符串
- 如果类有公共属性使用Attributions描述属性
- 块注释和行注释
- 对于复杂的操作, 应该在其操作开始前写上若干行注释
- 对于不是一目了然的代码, 应在其行尾添加注释.
class SampleClass(object):
"""Summary of class here.
Longer class information....
Longer class information....
Attributes:
likes_spam: A boolean indicating if we like SPAM or not.
eggs: An integer count of the eggs we have laid.
"""
def __init__(self, likes_spam=False):
"""Inits SampleClass with blah."""
self.likes_spam = likes_spam
self.eggs = 0
def public_method(self):
"""Performs operation blah."""
-
TODO注释
- 为临时代码使用TODO注释
- 为将来要做的某件事使用TODO注释,并确保包含了一个具体日期或触发条件
-
字符串
- 使用%和format方法格式化字符串
- 避免在循环中使用 '+' 或 '+='连接字符串,使用列表保存,结束后用join连接
- 同一文件中,保持字符串引号一致性,要么全部使用单引号,要么全部使用双引号
- 为多行字符串使用三重双引号
-
类
- 如果一个类不能显式的继承自其它类,那么将显式继承object类
- 内部类也遵循以上规则
-
文件和sockets
- 推荐使用with语句管理文件和类文件对象
- 不支持with语句的对象可使用contextlib.closing()显示关闭
-
导入格式
- 每个导入独占一行
- 导入语句在文件顶部
- 导入顺序分别为:标准库、第三方库、指定文件
-
命名
Type | Public | Internal |
---|---|---|
Modules | lower_with_under | _lower_with_under |
Packages | lower_with_under | |
Classes | CapWords | _CapWords |
Exceptions | CapWords | |
Functions | lower_with_under() | _lower_with_under() |
Global/Class Constants | CAPS_WITH_UNDER | _CAPS_WITH_UNDER |
Global/Class Variables | lower_with_under | _lower_with_under |
Instance Variables | lower_with_under | _lower_with_under (protected) or __lower_with_under (private) |
Method Names | lower_with_under() | _lower_with_under() (protected) or __lower_with_under() (private) |
Function/Method Parameters | lower_with_under | |
Local Variables | lower_with_under |
编码处理
-
python2中unicode表示字符串,str表示字节码
-
python3中str表示字符串,bytes表示字节码
-
不要对字符串进行decode, 不要对字节码进行encode
-
输入时将所有的字符转为字符串进行处理,输出时根据不同平台进行编码
- decode early
- uicode(py2), str(py3) everywhere
- encode later
-
声明文件编码
# -*- coding: utf-8 -*-
-
获取和修改系统默认编码
import sys
print(sys.getdefaultencoding())
sys.setdefaultencoding('utf-8') # 慎用此方法
print(sys.getdefaultencoding())
魔方方法
- 基本定制
class A():
def __init__(self): # 初始化实例
print "call __init__"
self.a = 1
def __new__(cls): # 构造函数,创建实例
print "call __new__"
def __del__(self): # 析构函数,删除实例
print "call __del__"
def __str__(self): # 打印字符输出,内建函数str()
print "call __str__"
return "class A str"
def __repr__(self): # 运行时字符串输出,内建函数repr()
print "call __repr__"
return "class A repr"
def __unicode__(self): # 返回字符串,内建函数unicode()
print "call __unicode__"
return "class A unicode"
def __nozero__(self): # obj的布尔值,内建函数bool()
print "call __nozero__"
return 1
def __len__(self): # 返回obj的长度,内建函数len()
print "call __len__"
return 1
def __call__(self, *args): # 对象是否可调用
print "call __call__"
a = A()
print a
repr(a)
unicode(a)
print(bool(a))
print(len(a))
print(callable(a))
-
__init__和 __new__
- __new__ 创建对象实例,相当于构造函数,先调用
- __init__ 初始化实例对象,给对象赋值,后调用
-
对象比较
class A():
def __init__(self, value):
self.value = value
def __cmp__(self, obj): # 内建cmp()
print "call __cmp__"
return self.value - obj.value
def __lt__(self, obj): # <
print "call __lt__"
return self.value < obj.value
def __gt__(self, obj): # >
print "call __gt__"
return self.value > obj.value
def __eq__(self, obj): # ==
print "call __eq__"
return self.value == obj.value
a1 = A(1)
a2 = A(2)
print(cmp(a1,a2))
print(a1 < a2)
print(a1 > a2)
print(a1 == a2)
- 属性操作
class A():
def __init__(self):
self.value = 1
def __getattr__(self, name): # 内建getattr();仅当属性没有找到时调用
print "call __getattr__"
try:
return self.__dict__[name]
except:
return "not found"
def __setattr__(self, name, value): # 内建setattr()
print "call __setattr__"
self.__dict__[name] = value
def __delattr__(self, name):
print "call __delattr__"
del self.__dict__[name]
def __getattribute__(self, name): # 内建getattr(),总是被调用
print "call __getattribute__"
return self.__dict__[name]
def __get__(self, name): # 描述符
pass
def __set__(self, name, value):
pass
def __del__(self):
pass
a = A()
print(getattr(a, "value"))
print(getattr(a, "name"))
del a.value
-
get、getattr、getattribute
- __getattribute__ 无条件被调用,通过实例访问属性
- __getattr__ 仅当属性找不到时调用,返回一个值或AttributionError
- __get__ 定义了该方法的类自动变成描述符
- 描述符是为处理重复的属性方法,编写单独的类来处理
- 定义了__get__,__set__,__delete__,__set_name__方法的为数据描述符,只定义了__get__方法的为非数据描述符
- 描述符要定义在类变量中
- 描述符类中使用弱引用(weakref)来存储实例属性,防止内存泄漏
- 使用实例对象访问属性时,默认调用__getattribute__方法,该方法查找属性的顺序依次为:类属性 -> 数据描述符 -> 实例属性 -> 非数据描述符 -> __getattr__
-
数值和二进制
C.__*add__(self, obj) # 加;+操作符
C.__*sub__(self, obj) # 减;-操作符
C.__*mul__(self, obj) # 乘;*操作符
C.__*div__(self, obj) # 除;/操作符
C.__*truediv__(self, obj) # True 除;/操作符
C.__*floordiv__(self, obj) # Floor 除;//操作符
C.__*mod__(self, obj) # 取模/取余;%操作符
C.__*divmod__(self, obj) # 除和取模;内建divmod()
C.__*pow__(self, obj[, mod]) # 乘幂;内建pow();**操作符
C.__*lshift__(self, obj) # 左移位;<<操作符
C.__*rshift__(self, obj) # 右移;>>操作符
C.__*and__(self, obj) # 按位与;&操作符
C.__*or__(self, obj) # 按位或;|操作符
C.__*xor__(self, obj) # 按位与或;^操作符
- 序列
C.__len__(self) # 序列中项的数目
C.__getitem__(self, ind) # 得到单个序列元素
C.__setitem__(self, ind,val) # 设置单个序列元素
C.__delitem__(self, ind) # 删除单个序列元素
C.__getslice__(self, ind1,ind2) # 得到序列片断
C.__setslice__(self, i1, i2,val) # 设置序列片断
C.__delslice__(self, ind1,ind2) # 删除序列片断
C.__contains__(self, val) f # 测试序列成员;内建in 关键字
C.__*add__(self,obj) # 串连;+操作符
C.__*mul__(self,obj) # 重复;*操作符
C.__iter__(self) # 创建迭代类;内建iter()
- 映射
C.__len__(self) # mapping 中的项的数目
C.__hash__(self) # 散列(hash)函数值
C.__getitem__(self,key) # 得到给定键(key)的值
C.__setitem__(self,key,val) # 设置给定键(key)的值
C.__delitem__(self,key) # 删除给定键(key)的值
C.__missing__(self,key) # 给定键如果不存在字典中,则提供一个默认值
装饰器
-
函数特点
- python中一切皆对象,函数也是对象
- 函数可作为参数和返回值
- 装饰器就是在不改变原有函数实现的前提下,给函数添加额外的功能
-
不带参数的装饰器
from functools import wraps
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
print('do something here')
return func(*args, **kwargs)
return wrapper
@decorator
def say(sth):
print('say %s' % sth)
- 带参数的装饰器
from functools import wraps
def decorator(arg)
def wrapper(func):
@wraps(func)
def inner_wrapper(*args, **kwargs):
print('do something here with %s' % arg)
return func(*args, **kwargs)
return inner_wrapper
return wrapper
@decorator('argument')
def say(sth):
print('say %s' % sth)
- 装饰器类
class decorator(object):
def __init__(self, arg='ARGU'):
self.arg = arg
def __call__(self, func):
def wrapper(*args, **kwargs):
print("%s: enter function %s()" % (self.arg, func.__name__))
func(*args, **kwargs)
return wrapper
@decorator(arg='ARGU')
def say(sth):
print('say %s' % sth)
-
内置装饰器
- @property定义一个属性
- @staticmethod, 定义一个静态方法,返回一个staticmethod对象
- @classmethod, 定义一个类方法,返回一个classmethod对象
- 不能在@staticmethod或@classmethod外再使用装饰器,因为他们返回不是一个callable对象
-
装饰器生效顺序
- 从上至下,由外到内
-
使用wrapt优化装饰器
import wrapt
def logging(level):
@wrapt.decorator
def wrapper(wrapped, instance, args, kwargs):
print "[{}]: enter {}()".format(level, wrapped.__name__)
return wrapped(*args, **kwargs)
return wrapper
@logging(level="INFO")
def do(work): pass
容器、可迭代对象、迭代器、生成器
- 关联关系
- 列表、集合、字典解析会生成一个容器
- 大部分容器是一个可迭代的对象, Bloom Filter(布隆过滤器)不是
- 可迭代的对象通过iter()方法可以返回一个迭代器
- 迭代器永远是一个可迭代的对象,迭代器通过next()方法返回元素
- 生成器是一种特殊的迭代器
-
容器
- 是一个把多个元素组织在一起的数据结构
- 大部分容器是可迭代的对象,容器中的元素可以逐个迭代获取,for ... in
- 可以通过in, not in 判断元素是否在容器中
- 常见容器对象 str, list, tuple, dict, set, frozenset, dequeue, nametuple, defaultdict, OrderedDict, Counter
-
可迭代对象
- 实现了__iter__()方法的对象,称之为可迭代对象
- 可迭代对象可通过for语句迭代
- 可迭代对象通过内置方法iter()返回一个迭代器
>>> x = [1, 2, 3]
>>> y = iter(x)
>>> z = iter(x)
>>> next(y)
1
>>> next(y)
2
>>> next(z)
1
>>> type(x)
<class 'list'>
>>> type(y)
<class 'list_iterator'>
- 迭代器
- 实现了__iter__()方法和__next__()方法的对象称之为迭代器
- __iter__()方法返回迭代器本身,__next__()方法返回容器中的下一个元素,如果没有将抛出StopIteration异常
- 迭代器内部保存一个状态,用来记录当前迭代的位置(位置指针)
- 迭代器是懒加载的,只有在每次询问使用下一个元素时才返回
- 已返回的元素是通过pop的方式
class Fib:
def __init__(self):
self.prev = 0
self.curr = 1
def __iter__(self):
return self
def __next__(self):
value = self.curr
self.curr += self.prev
self.prev = value
return value
>>> f = Fib()
>>> list(islice(f, 0, 10))
[1, 1, 2, 3, 5, 8, 13, 21, 34, 55]
- 生成器
- 生成器是一种特殊的迭代器,它的返回值不是通过return,而是通过yield
- 使用生成器表达式可以返回生成器对象 (x for x in seq)
- yield表达式是生成器的关键,它是生成器暂停恢复的点,代码从yield处恢复,又在下一个yield处暂停,使用send(value)方法可以将值显式的传给yield表达式
def fib():
prev, curr = 0, 1
while True:
yield curr
prev, curr = curr, curr + prev
>>> f = fib()
>>> list(islice(f, 0, 10))
[1, 1, 2, 3, 5, 8, 13, 21, 34, 55]
itertools
- 无限迭代器(count, cycle, repeat)
from itertools import *
# count(start=0, step=1), 返回一个生成器,生成从0开始的连续整数
for i in izip(count(1), ['a', 'b', 'c']):
print(i)
(1, 'a')
(2, 'b')
(3, 'c')
# cycle(iterable) 返回一个生成器,对可迭代对象中的元素重复
i = 0
for item in cycle(['a', 'b', 'c']):
i += 1
print('%d, %s' % (i, item))
if i == 6:
break
(1, 'a')
(2, 'b')
(3, 'c')
(4, 'a')
(5, 'b')
(6, 'c')
# repeat(obj, times=None) 重复obj指定次数,未指定无限重复
for i in repeat('loda', 3):
print(i)
'loda'
'loda'
'loda'
- 序列处理迭代器(chain, compress, groupby, ifilter, islice, imap, izip, tee)
from itertools import *
# chain(*iterables), 将多个迭代器合并,产生所有迭代器的元素
for i in chain([1, 2, 3], ('a', 'b')):
print(i)
1
2
3
'a'
'b'
# compress(iterable, selector) 根据selector对原始数据筛选
data = ['a', 'b', 'c', 'd', 'e']
selector = [1, 0, 1, 0, 0]
for i in compress(data, selector):
print(i)
'a'
'c'
# groupby(iterable, key) 返回一个按照key进行分组后的值集合的迭代器
a = ['aa', 'ab', 'abc', 'bcd', 'abcde']
for i, k in groupby(a, len):
print i, list(k)
2 ['aa', 'ab']
3 ['abc', 'bcd']
5 ['abcde']
# ifilter(filter_func, iterable) 返回一个filter函数过滤后为true的项 ifilterfalse相反
for i in ifilter(lambda x: x > 1, [ -1, 0, 1, 2, 3, 4, 1, -2 ]):
print(i)
2
3
4
# islice(iterable, start, stop[, step]) 切片返回迭代器
for i in islice(count(), 0, 10, 3):
print(i)
0
3
6
9
# imap(func, *iterables) 使用func作用于每一项,返回一个迭代器
for i in imap(lambda x: x * 2, xrange(3)):
print(i)
0
2
4
# izip(*iterables) 合并多个迭代对象为元组,并返回该迭代器
for i in izip(['a', 'b', 'c'], [1, 2, 3]):
print(i)
('a', 1)
('b', 2)
('c', 3)
# tee(iterable[, n=2]) 返回n个独立的迭代器,迭代器和原始迭代器的元素一样
i1, i2 = tee(islice(count(), 1, 4))
for i in i1:
print(i)
for i in i2:
print(i)
1
2
3
1
2
3
- 组合生成器(prduct, permutations, combinations)
from itertools import *
# product(*iterable) # 生成多个迭代器的笛卡尔积
c = product((1, 2, 3), ('a', 'b'))
for i in c:
print(i)
(1, 'a')
(1, 'b')
(2, 'a')
(2, 'b')
(3, 'a')
(3, 'b')
# permutations(iterable[,r]) 返回序列中任意r个元素的全排列
permutations('ABCD', 2) --> AB AC AD BA BC BD CA CB CD DA DB DC
# combinations(iterable[,r]) 返回序列中任意r个元素的组合(不含重复值)
combinations('ABCD', 2) --> AB AC AD BC BD CD
functools
- functools.partial
# 通过包装手法,允许我们 “重新定义” 函数签名
# 用一些默认参数包装一个可调用对象,返回结果是可调用对象,并且可以像原始对象一样对待
import functools
def add(a, b):
return a + b
add(4, 2)
6
plus3 = functools.partial(add, 3)
plus5 = functools.partial(add, 5)
plus3(4)
7
plus5(10)
15
- functools.update_wrapper
# 默认partial对象没有name和doc
# 使用update_wrapper(),从原始对象拷贝name、module、doc和 dict 加入现有partial对象
#!/usr/bin/env python
# encoding: utf-8
def wrap(func):
def call_it(*args, **kwargs):
"""wrap func: call_it"""
print 'before call'
return func(*args, **kwargs)
return call_it
@wrap
def hello():
"""say hello"""
print 'hello world'
from functools import update_wrapper
def wrap2(func):
def call_it(*args, **kwargs):
"""wrap func: call_it2"""
print 'before call'
return func(*args, **kwargs)
return update_wrapper(call_it, func)
@wrap2
def hello2():
"""test hello"""
print 'hello world2'
if __name__ == '__main__':
hello()
print hello.__name__
print hello.__doc__
print
hello2()
print hello2.__name__
print hello2.__doc__
before call
hello world
call_it
wrap func: call_it
before call
hello world2
hello2
test hello
- functools.wraps
# 同update_wrapper
from functools import wraps
def wrap3(func):
@wraps(func)
def call_it(*args, **kwargs):
"""wrap func: call_it2"""
print 'before call'
return func(*args, **kwargs)
return call_it
@wrap3
def hello3():
"""test hello 3"""
print 'hello world3'
内存管理
-
引用计数
- 引用计数增加
- 对象被创建 a = 2
- 对象被引用 b = a
- 对象作为函数参数 sys.getrefcount(a)
- 对象作为容器元素 lst = [1, a, b]
- 引用计数减少
- 对象被显式销毁 del a
- 对象引用发生变化 a = 2, a = []
- 对象作用域结束
- 容器被销毁或对象被容器删除
- 引用计数为0的对象会被垃圾回收
- 引用计数增加
-
标记清除
- 解决循环引用的问题
- 标记
- 标记阶段,从根节点(调用栈、寄存器、全局变量)出发,遍历所有的对象,如果可达(还有其他对象引用),则标记为reachable
- 维护一个引用计数副本,如果有对象引用自己,则遍历时副本引用计数-1(解环),副本引用计数为0的对象标记为unreachable,不可达
- 调整,从可达对象出发,它所引用的所有的对象都标记为可达
- 清除
- 清除阶段再次遍历所有对象,将不可达的对象清除
-
分代回收
- 以空间换时间,较少垃圾回收的次数,提高回收效率(存在越久的对象越不可能是垃圾)
- 对象存在三代(0新, 1青, 2老),新建的对象都处在第0代,当新建对象和释放对象之间的差值到达某个阈值时,自动触发垃圾回收,未被回收的对象会移动到下一代
- 经过N次0代回收后会进行一次0代和1代回收
- 经过N次1代回收后会进行一次0代 1代 2代回收
- 默认值是(700, 10 ,10), gc.get_threshold()
网友评论