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))