代码重构之路 --- 一些编程规范和 python idiom make your code more pythonic

蓝色的 mandelbrot

1. 重构代码(《重构》的python实现):

常量和临时变量

  • 提取常量
  • 加入解释性变量
  • 分解临时变量(单一原则)
  • 去除临时变量
  • 移除控制标记(直接return 或 break)

函数

  • 拆分
  • 去除(去除简单的)
  • 合并多个函数,使用参数
  • 函数不应有副作用,单一职责原则,一个函数不应做两件事,函数粒度尽量小

表达式

  • 过多的条件逻辑, 难以理解正常的执行路径. 在python中的特征是, 缩进太深(尽早return 改为平行的 if)
  • 合并条件表达式(对于返回结果相同的条件)
  • 分解复杂条件表达式(分别从if,else中提炼出独立函数)
  • 合并重复的代码片段(if 和 else都有的代码,放到外面去)

参数及返回值

  • 提取对象(如果参数/返回值是一组相关的数值, 且总是一起出现, 可以考虑提取成一个对象)
  • 减少参数(中间不需要的步骤)
def get_width_height():
    ....

    return width, height

def get_area(width, height):
    return width, height
# to
class Rectangle(object):
    def __init__(self, width, height):
        self.width = width
        self.height = height

    def area(self):
        return self.width * self.height

def get_shape():
    ....
    return Rectangle(height, width)

  • 搬移,函数/字段
  • 拆分类
  • 去除类 (类做的事不多,不再独立存在)

模式

  • 慎用,只用熟悉的,只用符合业务场景的
  • 装饰器

参考:http://www.wklken.me/posts/2017/06/17/refactoring-07.html#yuan-ze

2. pycharm重构快捷键

序号 快捷键 功能
1 F5 复制文件
2 F6 移动文件
3 SHIFT F6 重命名
4 ALT delete 安全删除
5 CTRL F6 改变函数形参
6 CTRL ALT M 将代码提取为函数
7 CTRL ALT V 将代码提取为变量
8 CTRL ALT C 将代码提取为常数
9 CTRL ALT F 将代码提取为字段
10 CTRL ALT P 将代码提取为参数

3. python idiom, pythonic

每当你在代码库中看到以下的模式可以参照以下的建议进行重构,让代码变得更加的pythonic,可读性更好,更容易维护。
参考:Python重构代码的一些模式 http://mpwang.github.io/2017/08/26/python-refactor-patterns/

enumerate

需要使用列表的下标时,不要使用C风格的下标遍历

lst = ['a', 'b', 'c']
# DON'T
i = 0
for i in lst:
    print i, '-->', lst[i]
    i += 1
# OR
for i in range(len(lst)):
    print(i, '-->', lst[i])
# DO
for idx, item in enumerate(lst):
    print(idx, '-->', item)

zip/izip

同时遍历两个列表时,不要使用C风格的下标遍历

lst1 = ['a', 'b', 'c']
lst2 = [1, 2, 3]
# DON'T
for i in range(len(lst1)):
    print(lst1[i])
    print(lst2[i])
# DO
for lst1_item, lst2_item in zip(lst1, lst2):
    print(lst1_item)
    print(lst2_item)
# BETTER
# 不需要在内存中生成包含lst, lst2的第三个列表
from itertools import izip
for lst1_item, lst2_item in izip(lst1, lst2):
    print(lst1_item)
    print(lst2_item)

unpacking tuple

x, y = y, x
foo, bar, _ = words # 使用 _ 如果你不需要这个值

Dict.setdefault/defaultdict

处理字典中key不存在时的默认值

# group words by frequency
words = [(1, 'apple'), (2, 'banana'), (1, 'cat')]
frequency = {}
# DON'T
for freq, word in words:
    if freq not in frequency:
        frequency[freq] = []
    frequency[freq].append(word)
# DO
for freq, word in words:
    frequency.setdefault(freq, []).append(word)
# BETTER
from collections import defaultdict
frequency = defaultdict(list)
for freq, word in words:
    frequency[freq].append(word)
# 在工作中要经常处理字典为空或键值不存在的情形,用get和setdefault代替dict_name['key']

setdefault vs get
setdefault()的使用,类似get方法,如果字典中包含有给定键,则返回该键对应的值,否则返回为该键设置的值
不同点:1.setdefault会把不存在的item保存到原来的dict,2.setdefault比get快10percent

person_dict = {}
person_dict['liqi'] = 'LiQi'
person_dict.setdefault('liqi', 'Liqi')  # 'LiQi'
person_dict.setdefault('Kim', 'kim')  # 'kim'
person_dict
person_dict.get('Dim', 'D')  # 'D'
person_dict  # {'liqi': 'LiQi', 'Kim': 'kim'}

Dict.iteritems

遍历字典

words = {'apple': 1, 'banana': 2, 'cat': 3}
# OK
for word in words:
    print word, '-->', words[word] # 需要计算word的hash值
# GOOD
for word, freq in words.items():
    print word, '-->', freq
# BETTER
# 不需要在内存中生存包含words所有元素的中间结果
for word, freq in words.iteritems():
    print word, '-->', freq

for...else

break and nobreak

# DO
for word in words:
    if condition(word):
        # 处理存在符合condition的元素的情况
        print 'Found'
        break
else:
    # 处理没有符合condition元素的情况
    print 'Not found'

try...except...else

分开异常处理与正常情况

# GOOD
try:
    result = json.loads(get_external_json())
    do_something_with(result)
except Exception as e:
    handle_error(e)
# BETTER
try:
    # 异常可能抛出点
    result = json.loads(get_external_json())
except Exception as e:
    # 异常处理
    handle_error(e)
else:
    # 正常情况
    do_something_with(result)

https://medium.com/the-andela-way/idiomatic-python-coding-the-smart-way-cc560fa5f1d6

1. chained comparison operators

# bad
if x <= y and y <= z:
  # do something
# good
if x <= y <= z:
  # do something

2. indentation(if else )

3. use the falsy & truthy concepts

For example an empty list/sequences [], empty dictionaries {} None, False, Zero for numeric types, are considered “falsy”. On the other hand, almost everything else is considered “truthy”.

# bad
x = True
y = 0
if x == True:
  # do something
elif x == False:
  # do something else
if y == 0:
  # do something
ls = [2, 5]
if len(ls) > 0:
  # do something
# good
(x, y) = (True, 0)
# x is truthy
if x:
  # do something
else:
  # do something else
# y is falsy
if not y:
  # do something
ls = [2, 5]
if ls:
  # do something

4. ternary operator replacement

# bad
a = True
value = 0
if a:
  value = 1
print(value)
# good
a = True
value = 1 if a else 0
print(value)

5. use the 'in' keyword

# bad
city = 'Nairobi'
found = False
if city == 'Nairobi' or city == 'Kampala' or city == 'Lagos':
  found = True
# good
city = 'Nairobi'
found = city in {'Nairobi', 'Kampala', 'Lagos'}

6. Use ‘return’ to evaluate expressions, in addition to return values

# bad
def check_equal(x, y):
  result = False

  if x == Y:
    result = True
  return result
# good
def check_equal(x, y):
  return x == y

7. multiple assignment

# good
x = y = z = 'foo'

8. formatting strings

def user_info(user):
  return 'Name: {user.name} Age: {user.age}'.format(user=user)

9. list comprehension(set, dict)

ls = [element for element in range(10) if not(element % 2)]
emails = {user.name: user.email for user in users if user.email}

10. sets

ls1 = [1, 2, 3, 4, 5]
ls2 = [4, 5, 6, 7, 8]
elements_in_both = list( set(ls1) & set(ls2) )
print(elements_in_both)

11. dont't repeat yourself(dry)

# bad
if user:
  print('------------------------------')
  print(user)
  print('------------------------------')
# good
if user:
  print('{0}
{1}
{0}'.format('-'*30, user))

原文地址:https://www.cnblogs.com/bruspawn/p/9236379.html