エキスパートPythonプログラミング読書会02に参加しました

エキスパートPythonプログラミング読書会02 : ATND に参加して講師のお手伝いをしてきました。

今回の読書会でお話できたのは第2章のうち以下の内容だけでした。

  • リスト内包表記
  • イテレータとジェネレータ
    • ジェネレータ
    • コルーチン
    • ジェネレータ式


準備不足で要領よく進行できず、すみませんでした。実際にやってみた雰囲気や進め方など、次回への改善に繋げていきます。2章全般の資料を読書会の前にアップロードして公開していますが、次回まで時間があるので後半部分は修正すると思います。2章全般の内容で、

この内容を詳細に知りたい!
この内容がよく分からなかった(> <)

などなど、何かしらありましたら Twitter で @ か、ハッシュタグ #expertpython を付けてコメントください。

スライド資料、サンプルコードは
2010/9/29(水) に少し修正しました。

読書会で使用したサンプルコードです。

  • 01
    • リスト内包表記の使用例とデータ変換
def same_value_tuple_with_list_comprehension(seqs):
    """
    >>> same_value_tuple_with_list_comprehension(range(3))
    [(0, False), (1, False), (2, False)]
    """
    return [(i, False) for i in seqs]

def convert_tuple_to_dict(seqs):
    """
    >>> convert_tuple_to_dict(range(3))
    {0: False, 1: False, 2: False}
    """
    return dict(same_value_tuple_with_list_comprehension(seqs))

def convert_list_to_json(seqs):
    """
    >>> convert_list_to_json(range(3))
    '[[0, false], [1, false], [2, false]]'
    """
    import json
    return json.dumps(same_value_tuple_with_list_comprehension(seqs))

def convert_dict_to_json(seqs):
    """
    >>> convert_dict_to_json(range(3))
    '{"0": false, "1": false, "2": false}'
    """
    import json
    return json.dumps(convert_tuple_to_dict(seqs))

def list_comprehension_usecase():
    """
    >>> list_comprehension_usecase()
    '[{"last_login": "2010-09-05 18:30:45", "name": "user1"},
      {"last_login": "2010-09-07 13:55:10", "name": "user2"}]'
    """
    import json
    from datetime import datetime
    class TestObj(object):
        def __init__(self, id, last_login):
            self.id = id
            self.last_login = last_login

    from_conversion_table = {
        1:  "user1",
        2:  "user2",
    }

    from_or_mapper = [
        TestObj(1, datetime(2010, 9, 5, 18, 30, 45)),
        TestObj(2, datetime(2010, 9, 7, 13, 55, 10)),
    ]

    data = [dict(
        name       = from_conversion_table.get(obj.id),
        last_login = obj.last_login.strftime('%Y-%m-%d %H:%M:%S'),
        ) for obj in from_or_mapper]
    # data = []
    # for obj in from_or_mapper:
    #     name = from_conversion_table.get(obj.id)
    #     last_login = obj.last_login.strftime('%Y-%m-%d %H:%M:%S')
    #     data.append(dict(name=name, last_login=last_login))

    return json.dumps(data)
  • 02
    • enumerate() の使用例
def read_enumerate(path):
    """
    >>> read_enumerate("./tests/dmesg.log")
    642
    """
    # ファイルの行数を数える処理にも応用できる
    cnt = -1
    for cnt, line in enumerate(open(path, "rb")):
        pass
    cnt += 1
    return cnt
  • 03
    • 関数をジェネレータに変更
def toggle(obj):
    """
    >>> g = toggle(True)
    >>> next(g)
    False
    >>> next(g)
    True
    >>> next(g)
    False
    """
    while True:
        obj = not obj
        yield obj

def search_file_normal(path, file):
    """
    >>> search_file_normal('./tests', '*.txt')
    ['./tests/abc.txt', './tests/def.txt']
    """
    import os
    import glob
    from os.path import join
    found = []
    for root, dirs, files in os.walk(path):
        for match in glob.glob(join(root, file)):
            found.append(match)
    return found

def search_file_gen(path, file):
    """
    >>> [f for f in search_file_gen("./tests", "*.txt")]
    ['./tests/abc.txt', './tests/def.txt']
    
    >>> g = search_file_gen("./tests", "*.txt")
    >>> next(g)
    './tests/abc.txt'
    >>> next(g)
    './tests/def.txt'
    >>> next(g)
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    StopIteration
    """
    import os
    import glob
    from os.path import join
    for root, dirs, files in os.walk(path):
        for match in glob.glob(join(root, file)):
            yield match
  • 04
    • 任意のディレクトリ配下のサイズとファイル数を調べる
def get_files_and_size_normal(origin_dir):
    """
    >>> get_files_and_size_normal("./test")
    (3, 12L)
    """
    import os
    from os.path import getsize, join
    file_num, size = 0, 0
    for root, dirs, files in os.walk(origin_dir):
        size += sum(getsize(join(root, name)) for name in files)
        file_num += len(files)
    return file_num, size

def get_files_and_size_gen(origin_dir):
    """
    >>> g = get_files_and_size_gen("./test")
    >>> next(g)
    (1, 4L)
    >>> next(g)
    (2, 8L)
    
    >>> for file_num, size in get_files_and_size_gen("./test"):
    ...     print file_num, size
    1 4
    2 8
    3 12
    """
    import os
    from os.path import getsize, join
    file_num, size = 0, 0
    for root, dirs, files in os.walk(origin_dir):
        size += sum(getsize(join(root, name)) for name in files)
        file_num += len(files)
        yield file_num, size
  • 05
    • ジェネレータの使用例
def read_file_normal(path, mode):
    """
    >>> read_file_normal("./test/test.txt", "r")
    abc
    <BLANKLINE>
    <BLANKLINE>
    <BLANKLINE>
    def
    <BLANKLINE>
    #comment
    <BLANKLINE>
    ghi
    <BLANKLINE>
    """
    for line in file(path, mode):
        print line # something to do

def read_file_without_linebreak(path, mode, charset="utf-8"):
    """
    >>> for line in read_file_without_linebreak("./test/test.txt", "r"):
    ...     print line
    abc
    <BLANKLINE>
    def
    #comment
    ghi
    """
    import io
    with io.open(path, mode=mode, encoding=charset) as file_obj:
        for line in file_obj:
            yield line.rstrip("\r\n")

def read_file_without_comment(path, mode, charset="utf-8"):
    """
    >>> for line in read_file_without_comment("./test/test.txt", "r"):
    ...     print line
    abc
    def
    ghi
    """
    import io
    with io.open(path, mode=mode, encoding=charset) as file_obj:
        for line in file_obj:
            if ''.join(line.split()) and not line.startswith("#"):
                yield line.rstrip("\r\n")

def pattern_matching(path, mode):
    """
    >>> for match in pattern_matching("./test/test.txt", "r"):
    ...     print match
    ab
    gh
    """
    import re
    regexp = re.compile("ab|gh")
    for line in read_file_without_comment(path, mode):
        for match in regexp.findall(line):
            yield match
  • 06
    • send メソッドを使用してジェネレータへ値を渡す簡単な例
def echo_upper():
    """
    >>> g = echo_upper()
    >>> next(g)
    >>> for i in "abc":
    ...     g.send(i)
    A
    B
    C
    >>>
    >>> g = echo_upper()
    >>> for i in [None, "a", "b", "c"]:
    ...     g.send(i)
    A
    B
    C
    """
    while True:
        data = yield
        print data.upper()
    • 値を生成する yield と式の右辺になる yield の2つが出てくる例
def echo_upper_with_2yields():
    """
    >>> g = echo_upper_with_2yields()
    >>> for i in "abc":
    ...     next(g)
    ...     print g.send(i)
    A
    B
    C
    >>>
    >>> g = echo_upper_with_2yields()
    >>> for i in [None, "a", None, "b", None, "c"]:
    ...     print g.send(i)
    None
    A
    None
    B
    None
    C
    """
    while True:
        data = yield
        yield data.upper()

def echo_upper_with_2roles():
    """
    >>> g = echo_upper_with_2roles()
    >>> for i in [None, "a", "b", "c"]:
    ...     print g.send(i)
    INITIAL
    A
    B
    C
    """
    data = "initial"
    while True:
        data = yield data.upper()
    • yield でユーザ認証を作ってみた(動作確認用サンプルであり、実用的ではありません)
def authenticate_simple():
    from getpass import getpass
    secret_table = {
        "t2y": "secret",
    }
    user = yield raw_input("Enter username: ")
    while True:
        passwd = yield getpass("passwd: ")
        if secret_table.get(user) == passwd:
            return

def main():
    g = authenticate_simple()
    r = None
    try:
        while True:
            r = g.send(r)
    except StopIteration:
        pass
    print "Authenticated"

if __name__ == "__main__":
    main()

エキスパートPythonプログラミング

エキスパートPythonプログラミング