将代码转换为漂亮的,地道的Python代码[操作指南,干货]

参考: Raymond Hettinger在PyCon US 2013上的演讲 Transforming Code into Beautiful, Idiomatic Python

立即改变

  • 将传统的使用索引i的循环方式改变为python core的习惯方式
  • 学习for-else语句,以及带有两个参数形式的iter()之类的高级技术
  • 提升自己的技艺,并且追求干净、快速、地道的python代码

遍历一串顺序的数字:

for i in [0, 1, 2, 3, 4, 5]:
    print i**2

for i in range(6):
    print i**2

for i in xragne(6):    #xrange是range的iter版
    print i**2

遍历一个数组:

colors = ['red', 'green', 'blue', 'yellow']
for i in range(len(colors)):
    print colors[i]

for color in colors:
    print color

反向遍历:

colors = ['red', 'green', 'blue', 'yellow']
for i in range(len(colors)-1, -1, -1):
    print colors[i]

for color in reversed(colors):
    print color

遍历同时获取索引编号:

colors = ['red', 'green', 'blue', 'yellow']
for i in range(len(colors)):
    print i, '-->', colors[i]

for i, color in enumerate(colors):
    print i, '-->', colors[i]

遍历两个数组:

names = ['raymond', 'rachel', 'matthew']
colors = ['red', 'green', 'blue', 'yellow']
n = min(len(names), len(colors))
for i in range(n):
    print names[i], '-->', colors[i]

for name, color in zip(names, colors):
    print name, '-->', color

for name, color in izip(names, colors):   #izip是zip的iter版
    print name, '-->', color

遍历排序数组:

colors = ['red', 'green', 'blue', 'yellow']
for color in sorted(colors):
    print color

for color in sorted(colors, reverse=True):
    print color

自定义排序:

colors = ['red', 'green', 'blue', 'yellow']
def compare_length(c1, c2):
    if len(c1) < len(c2): return -1
    if len(c1) > len(c2): return 1
    return 0
print sorted(colors, cmp=compare_length)   #compare_length在每次比较时都要调用,总次数为nlog(n)

print sorted(colors, key=len)    #效率更高,只需调用n次

循环遍历时在特定条件下调用函数:

blocks = []
while True:
    block = f.read(32)
    if block == '':
        break
    blocks.append(block)

blocks = []
#双参数的iter(),第二个参数是哨兵变量,一旦和哨兵变量相同就break
for block in iter(partial(f.read, 32), ''): #partial用于给read传递参数
    blocks.append(block)

区别出循环中的多个退出点

def find(seq, target):
    found = False
    for i, value in enumerate(seq):
        if value == target:
            found = True
            break
    if not found:
        return -1
    return 1

def find(seq, target):
    for i, value in enumerate(seq): #使用for...else...
        if value == target:
            break
    else:
        return -1
    return 1

dict的技巧

  • 熟练掌握dict是一项基础的python技能
  • 这是一个基础的工具用于表达关系,连接,计数和分组

遍历dict的key

d = {'matthew': 'blue', 'rachel': 'green', 'raymond': 'red'}
for k in d:
    print k

for k in d.keys():
    if k.startswith('r'):
        del d[k]

d = {k : d[k] for k in d if not k.startswith('r')}

遍历dice的key和value

for k in d:
    print k, '-->', d[k]

for k, v in d.items():
   print k, '-->', v

for k, v in d.iteritems():   #iteritems是items的iter版
    print k, '-->', v

通过pair创建dict

names = ['raymond', 'rachel', 'matthew']
colors = ['red', 'green', 'blue']

d = dict(izip(names, colors))

使用dict对list中元素计数

colors = ['red', 'green', 'red', 'blue', 'green', 'red']
d = {}
for color in colors:
    if color not in d:
        d[color] = 0
    d[color] += 1

d = {}
for color in colors:
    #使用get来减少了对元素是否存在的判断
    d[color] = d.get(color, 0) + 1

d = defaultdict(int)
for color in colors:
    d[color] += 1

使用dict对list中元素分组

name = ['raymond', 'rachel', 'matthew', 'roger', 'betty', 'melissa', 'judith', 'charlie']

d = {}
for name in names:
    key = len(name)
    if key not in d:
        d[key] = []
    d[key].append(name)

d = {}
for name in names:
    key = len(name)
    d.setdefault(key, []).append(name)

d = defaultdict(list)
for name in names:
    key = len(name)
    d[key].append(name)

提升可读性

  • 位置或者索引参数是不错
  • 但是使用keywords以及names更好
  • 第一种方法对计算机来说很方便
  • 第二种方式更适应人的思考

调用函数时使用参数名来传参使得表述更清晰

twitter_search('@obama', False, 20, True)

twitter_search('@obama',  retweets=False, numtweets=20, popular=True)

当函数返回多个值得时候请用named tuples

doctest.testmod()
(0,4)

doctest.testmod()
TestResults(failed=0, attempted=4)

TestResults = namedtuple('TestResults', ['failed', 'attempted'])

Unpacking sequences

p = 'Raymond', 'Hettinger', 0x30, 'python@example.com'
fname = p[0]
lanme = p[1]
age = p[2]
email = p[3]

fname, lname, age, email = p

同时更新多个变量

def fibonacci(n):
    x = 0
    y = 1
    for i in range(n):
        print x
        t = y
        y = x + y
        x = t

def fibonacci(n):
    x, y = 0, 1
    for i in range(n):
        print x
        x, y = y, x+y

tuple的packing和unpacking

  • 不要低估这种同时改变变量的优点,这可以避免很多由于变量更新顺序造成的错误,尤其是在做一些迭代的数值运算的时候
tmp_x = x + dx * t
tmp_y = y + dy * t
tmp_dx = influence(m, x, y, dx, dy, partial='x')
tmp_dy = influence(m, x, y, dx, dy, partial='y')
x = tmp_x
y = tmp_y
dx = tmp_dx
dy = tmp_dy

x, y, dx, dy = (x + dx * t,
                y + dy * t,
                influence(m, x, y, dx, dy, partial='x'),
                influence(m, x, y, dx, dy, partial='y'))

字符串连接

names = ['raymond', 'rachel', matthew']
s = names[0]
for name in names[1:]:
    s += ', ' + name
print s

print ', '.join(names)

修改sequences

names = ['raymond', 'rachel', matthew']
del names[0]
names.pop(0)
names.insert(0, 'mark')

names = deque(['raymond', 'rachel', matthew'])
del names[0]
names.popleft()
names.appendleft('mark')

Decorator和Context Managers

  • 有利于拆分业务逻辑和控制逻辑
  • 干净,漂亮的工具来分解代码以及提升代码复用
  • 好的命名是最基本的
  • 记住:能力越大责任越大

使用decorators来拆解出控制逻辑

def web_lookup(url, saved={}):
    if url in saved:                       #控制逻辑
        return saved[url]
    page = urllib.urlopen(url).read()      #业务逻辑
    saved[url] = page
    return page

def cache(func):
    saved = {}
    @wraps(func)
    def newfunc(*args):
        if args in saved:
            return saved[args]
        result = func(*args)
        saved[args] = result
        return result
    return newfunc

分解出临时的上下文环境

old_context = getcontext().copy()
getcontext().prec = 50
print Decimal(355) / Decimal(113)
setcontext(old_context)

with localcontext(Context(prec=50)):
    print Decimal(355) / Decimal(113)

如何打开和关闭文件

f = open('data.txt')
try:
    data = f.read()
finally:
    f.close()

with open('data'.txt') as f:
    data = f.read()

如何使用locks

# Make a lock
lock = threading.Lock()

# Old-way to use a lock
lock.acquire()
try:
    print 'Critical section 1'
    print 'Critical section 2'
finally:
    lock.release()

# New-way to use a lock
with lock:
    print 'Critical section 1'
    print 'Critical section 2'

分解出临时的上下文环境

try:
    os.remove('somefile.tmp')
except OSError:
    pass

with ignored(OSError):
    os.remove('somefile.tmp')

# ignored的实现,在python 3.4中已支持
@contextmanager
def ignored(*exceptions):
    try:
        yield
    except exceptions:
        pass
with open('help.txt', 'w') as f:
    oldstdout = sys.stdout
    sys.stdout = f
    try:
        help(pow)
    finally:
        sys.stdout = oldstdout

with open('help.txt', 'w') as f:
    with redirect_stdout(f):
        help(pow)

@contextmanager
def redirect_stdout(fileobj):
    oldstdout = sys.stdout
    sys.stdout = fileobj
    try:
        yield fileobj
    finally:
        sys.stdout = oldstdout

list comprehensions 和 generator expressions

result = []
for i in range(10):
    s = i ** 2
    result.append(s)
print sum(result)

print sum([i ** 2 for i in range(10)])

print sum(i ** 2 for i in range(10))
comments powered by Disqus