代碼重構之路 --- 一些編程規範和 python idiom make your code more pythonic

藍色的 mandelbrot

1. 重構代碼(《重構》的python實現):

常量和臨時變量

  • 提取常量
  • 加入解釋性變量
  • 分解臨時變量(單一原則)
  • 去除臨時變量
  • 移除控制標記(直接return 或 break)html

    函數

  • 拆分
  • 去除(去除簡單的)
  • 合併多個函數,使用參數
  • 函數不該有反作用,單一職責原則,一個函數不該作兩件事,函數粒度儘可能小python

    表達式

  • 過多的條件邏輯, 難以理解正常的執行路徑. 在python中的特徵是, 縮進太深(儘早return 改成平行的 if)
  • 合併條件表達式(對於返回結果相同的條件)
  • 分解複雜條件表達式(分別從if,else中提煉出獨立函數)
  • 合併重複的代碼片斷(if 和 else都有的代碼,放到外面去)git

參數及返回值

  • 提取對象(若是參數/返回值是一組相關的數值, 且老是一塊兒出現, 能夠考慮提取成一個對象)
  • 減小參數(中間不須要的步驟)
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)

  • 搬移,函數/字段
  • 拆分類
  • 去除類 (類作的事很少,再也不獨立存在)github

    模式

  • 慎用,只用熟悉的,只用符合業務場景的
  • 裝飾器express

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

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風格的下標遍歷app

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風格的下標遍歷ide

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}\n{1}\n{0}'.format('-'*30, user))
相關文章
相關標籤/搜索