Note of 《Effective Python》(第六章)

记得以前上大学的时候, 去图书馆借了一本《代码简洁之道》. 虽然大部分的内容都忘得差不多了, 但里边的一些思想至今还是收益颇深.
最近开始看一本书叫做《Effective Python: 59 Specific Ways to Write Better Python》, 把里边一些印象深刻的东西记录在这篇日志里.
这篇文章记录的是第六章: <>

###Chapter 6: Built-in modules:

  • Item 42: Define Function Decorators with functools.wraps.
    主要就是Python中要是用decorator的话, 会有一些小问题.
    比如用一个helper去装饰fibonacci函数提高性能的话, help(fibonacci)显示的是decorator的信息.
    我写的一个wraps在fibonacci中应用例子方便理解:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    from functools import wraps


    def memo(f):
    c = {}

    '''wrapper function will copy all of the important metadata about the inner function to the outer function.
    ref: effective python - item 42
    print(fibonacci.__name__)
    '''
    @wraps(f)
    def helper(*args):
    if args not in c:
    c[args] = f(*args)
    return c[args]

    return helper


    @memo
    def fibonacci(n):
    if n <= 2:
    return 1
    else:
    return fibonacci(n-1) + fibonacci(n-2)
  • Item 43: Consider contextlib and with Statements for Reusable try/finally Behavior
    这个和之前看到的一个面试题挺有关系的, 就是with语句为什么会自己关闭. The with statement in Python is used to indecate when code is running in a special context. with 语句的诞生其实就是为了不重复写try/finally.
    这章讲的主要是上下文管理器, 我之前也很迷惑, 上下文管理器(context manager)到底是什么呢?
    我把我的理解写到下边这个自定义的方法里:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    from contextlib import contextmanager
    from urllib.request import urlopen

    @contextmanager
    def closing(thing):
    try:
    yield thing
    finally:
    thing.close()


    with closing(urlopen('http://www.python.org')) as page:
    for line in page:
    print(line)

上下文大致的意思就是: 代码块执行前的准备,代码块执行后的收拾.
一个类的实现:

1
2
3
4
5
6
7
8
9
10
11
# Implementing the Context Manager as a Class
class Saved(object):
def __init__(self, cr):
self.cr = cr

def __enter__(self):
self.cr.save()
return self.cr

def __exit__(self, type, value, traceback):
self.cr.restore()

  • Item 44: Make pickle Reliable with copyreg
    “The pickle built-in module can serialize Python objects into a stream of bytes and deserialize bytes back into objects. “
    有时候你要是看书看得云里雾里的时候不妨实践一下代码, 就能很快地理解了:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    import pickle

    class GameState(object):
    def __init__(self):
    self.level = 0
    self.lives = 4

    state = GameState()
    state.level += 1
    state.lives -= 1

    state_path = '/tmp/game_state.bin'
    with open(state_path, 'wb') as f:
    pickle.dump(state, f)

    with open(state_path, 'rb') as f:
    state_r = pickle.load(f)

    print(state_r.__dict__) # {'level': 1, 'lives': 3}


    # 但是问题来了, 如果类的属性扩展了, 那些旧的已保存的类要怎么办呢?
    # solution就是用copyreg built-in module.
    import copyreg

    class GameState(object):
    def __init__(self, level=0, lives=4, points=0):
    self.level = level
    self.lives = lives
    self.points = points


    def pickle_game_state(game_state):
    kwargs = game_state.__dict__
    return unpickle_game_state, (kwargs,)


    def unpickle_game_state(kwargs):
    return GameState(**kwargs)


    copyreg.pickle(GameState, pickle_game_state)

    state = GameState()
    state.points += 1000
    serialized = pickle.dumps(state)
    state_after = pickle.loads(serialized)
    print(state_after.__dict__) # {'level': 0, 'points': 1000, 'lives': 4}


    class GameState(object):
    def __init__(self, level=0, lives=4, points=0, magic=5):
    self.level = level
    self.lives = lives
    self.points = points
    self.magic = magic

    # deserializing an old GameState object:
    state_after = pickle.loads(serialized)
    print(state_after.__dict__) # {'points': 1000, 'level': 0, 'lives': 4, 'magic': 5}

Versioning Classes(略).
Stable Import Paths(略).

  • Item 45: Use datetime Instead of time for Local Clocks.
    Python有两种处理time zone的方法:
    • 旧方法: time built-in method, but is disastrously error conversions.
    • 新方法: datetime built-in module, work greatly with community-built package named pytz
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      from time import mktime
      from datetime import datetime, timezone

      # convert UTC to computer's local time
      now = datetime(2017, 2, 13, 15, 00, 00)
      now_utc = now.replace(tzinfo=timezone.utc)
      now_local = now_utc.astimezone()
      print(now_local)

      # convert local time back to a UNIX timestamp in UTC
      time_str = '2017-02-14 12:00:00'
      parse_format = '%Y-%m-%d %H:%M:%S'

      now = datetime.strptime(time_str, parse_format)
      time_tuple = now.timetuple()
      utc_now = mktime(time_tuple)
      print(utc_now)


      # pytz
      import pytz

      # Step 1: Convert local times to UTC first
      arrival_nyc = '2014-05-01 23:33:24'
      nyc_dt_naive = datetime.strptime(arrival_nyc, parse_format)
      eastern = pytz.timezone('US/Eastern')
      nyc_dt = eastern.localize(nyc_dt_naive)
      utc_dt = pytz.utc.normalize(nyc_dt.astimezone(pytz.utc))
      print(utc_dt)

      # Step 2: Convert to local time
      pacific = pytz.timezone('US/Pacific')
      sf_dt = pacific.normalize(utc_dt.astimezone(pacific))
      print(sf_dt)