使用 Function 代替 Class
如果一個 class 通常只被 instantiated 或是直接使用一次, 應該只需要寫成 function 就好.
1 2 3 4 5 6 7 8
class Greeting(object) def __init__(self, greeting='hello'): self.greeting = greeting def greet(self, name): return '%s! %s' % (self.greeting, name) greeting = Greeting('hola') print greeting.greet('bob')
如果常使用同樣的 argument 來 instance class, 用
functool.partial
來簡化.1 2 3 4 5 6 7 8 9 10 11
def greet(name): ob = Greeting('hola') print ob.greet('bob') return def greet(greeting, target): return '%s! %s' % (self.greeting, name) import functools greet = functools.partial(greet, 'hola') greet('bob)
當一個 class 只有 init 和一個 method, 就直接使用 function, 不要為了預留擴充性而使用 class, 等到真的要擴充的時候再 refactor 成 class 就好.
Namespace 應該力求單純 最好能做到 module → function/class
1
MuffinMail.MuffinHash.MuffinHash(foo=3)
Exception
- 盡可能重複使用
BookNotFoundError, CupNotFoundError 可以統一使用 NotFoundError - 使用 standard library 裡面已經有的
NotFoundError 可以直接使用 standard lib 的 LookupError - “exception” 不應該是 Exception 名稱的一部分
exception 已經是 exception object 了, 不需要額外再說一次:NotFoundException
應該直接用NotFound
- Sub Classing exception 可以降低 exception 數量 同時保持 catch 特定 exception 的能力
其他
- 小心過度簡化反而降低 reusability
- 用 class attributes 來作為 namespace 區隔是可以接受的 (like enum)
Example: Conway’s Game of Life
Class 版本
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
class Cell(object): def __init__(self, x, y, alive=True): self.x = x self.y = y self.alive = alive self.next = None def neighbors(self): yield (self.x + 1, self.y) yield (self.x + 1, self.y + 1) # ... yield (self.x + 1, self.y) class Board(object): def __init__(self): self.cells = {} # {(x,y): Cell()} def advance(self): for (x, y), cell in self.cells.items(): if len(cell.neighbors) > 3: cell.next = False
簡化: Function 版本
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
def neighbors(point): x, y = point yield x + 1, y yield x - 1, y yield x, y + 1 yield x, y - 1 yield x + 1, y + 1 yield x + 1, y - 1 yield x - 1, y + 1 yield x - 1, y - 1 def advance(board) newstate = set() recalc = board | set(itertools.chain(*map(neightbors, board))) for point in recalc: count = sum((neigh in board) for neigh in neighbors(point)) if count == 3 or (count ==2 and point in board): newstate.add(point) return newstate glider = set([(0, 0), (1, 0),(2, 0),(0, 1),(1, 2)]) for i in range(1000): glider = advance(glider) print glider
- board 其實就是一個 dictionary, (e.g. self.cells)
- alive 就是 boolean, 直接 check cell 是不是存在 dictionary 裡面就好了
- 不需要 next, 直接 create new dictionary 就好了
Attention: 新的做法中無法存取舊的數值. 那些不需要 class 的 operation, 等於是不需要保持這些資料的狀況, 就是簡化的時候.