def exec(self): # ------------------------------------------ # collections.namedtupleとtyping.NamedTuple # ----------------------------------------- # どちらも同じデータ構造を作り出す。 # バイトコードも同じとなる。 # ------------------------------------------ m1 = MyData(1, 'name01', 33) m2 = MyData2(1, 'name22', 22) pr('typing.NamedTuple', m1) pr('collections.namedtuple', m2) pr('getsizeof(typing.NamedTuple)', sys.getsizeof(m1)) pr('getsizeof(collections.namedtuple)', sys.getsizeof(m2)) try: m1.name = 'name02' except AttributeError as e: pr('typing.NamedTuple is immutable', e) try: m2.name = 'name02' except AttributeError as e: pr('collections.namedtuple is immutable', e) hr('dis.dis(MyData)') dis.dis(MyData.__new__) hr('dis.dis(MyData2)') dis.dis(MyData2.__new__)
def exec(self): ############################################# # datetime.datetime オブジェクトについて # # datetime.datetime オブジェクトは 日付を表す。 # このオブジェクトは日付処理を行う上で最低限必要な情報を # 各種備えている。 # # 現在日時を取得するには # datetime.now() # とする。 # (from datetime import datetime としているとする) ############################################# hr('datetime.now()') now = datetime.now() pr('now', now) pr('type', type(now)) # -------------------------------------------- # datetime.ctime() # -------------------------------------------- # 日付文字列を ctime スタイルで表示 # https://bit.ly/2PGoEYB # -------------------------------------------- hr('datetime.ctime()') ctime_value = now.ctime() pr('ctime', ctime_value) pr('type', type(ctime_value))
def exec(self): """サンプル処理を実行します。""" # -------------------------------------------------------------- # blinker は、イベント通知を行ってくれるライブラリ。 # 使い方がシンプルで速いのが特徴。 # Observerパターンを利用したい場合に有効。 # # blinker では、まず最初に signal() でシグナルを作成する。 # signal は、好きに命名することが可能 # 通知を受けるためには、 connect メソッドでアタッチする。 # あとは、 send メソッドで値を送信するたびにアタッチされたハンドラに通知が発行される。 # -------------------------------------------------------------- my_signal = bl.signal('my-signal') # sender を指定しない場合は ブロードキャスト 扱いとなる。 # つまり、この signal で発行されたすべての値を受け取ることが出来る。 my_signal.connect(self.print_message) hr('broadcast') for i in range(5): my_signal.send(i) hr('broadcast + filtering') # sender を指定している場合、値に合致したイベントのみが通知される。 my_signal.connect(self.print_message2, sender=3) for i in range(5): my_signal.send(i)
def exec(self): # ----------------------------------------------- # itertools.chain() # ---------------------- # 複数のシーケンスを繋いで一つのようにする。 # ----------------------------------------------- hr('it.chain()') list01 = ['hello', 'world'] list02 = list(range(10)) for x in it.chain(list01, list02, 'abc'): pr('value', x) # ----------------------------------------------- # itertools.zip_longest() # ---------------------- # 組み込み関数 zip() の別バージョン # 要素数が多い方に合わせる。 # # zip() は、要素が最も少ない方になる。 # ----------------------------------------------- hr('it.zip_longest()') for x, y in zip(list01, list02): pr('zip()', (x, y)) # zip_longest()は、要素が最も多い方になる for x, y in it.zip_longest(list01, list02): pr('zip_longest()', (x, y))
def exec(self): # ----------------------------------------- # 最も基本的な関数の形 # ----------------------------------------- func = Sample.normal_func hr(func.__name__) pr(func.__name__, func(10, 20)) # ----------------------------------------- # 可変引数あり # 可変引数は、慣習的に *args と記述する # ----------------------------------------- func = Sample.with_args hr(func.__name__) pr(func.__name__, func(10, 20, 30, 40, 50)) # ----------------------------------------- # デフォルト値あり # ----------------------------------------- func = Sample.with_default_value hr(func.__name__) pr(func.__name__, func(10, 20)) # ----------------------------------------- # キーワード引数あり # キーワード引数は、慣習的に **kwargs と記述する # ----------------------------------------- func = Sample.with_kwargs hr(func.__name__) pr(func.__name__, func(10, 20, z=100, zz=200)) # ----------------------------------------- # 特殊な アスタリクスのみ引数 を持つ関数 # ----------------------------------- # 引数リストの中に「*」のみの引数を定義すると # それ以降の引数について強制的にキーワード指定 # をして呼び出すように出来る。 # # 通常 func(x, y, z=100) のように定義して # いると、 func(10, 20, z=111) のように # 呼ぶことも出来るし、 func(10, 20, 111) と # 呼ぶことも出来る。 # # だが、func(x, y, *, z=100) と定義した場合 # func(10, 20, 111)と呼ぶとエラーとなり # func(10, 20) か func(10, 20, z=111) と # いうようにキーワード引数を明示的に指定しないと # 受け付けないように出来る。 # ----------------------------------------- func = Sample.with_asterisk_sentinel hr(func.__name__ + '(sentinel)') pr(func.__name__, func(10, 20)) pr(func.__name__, func(10, 20, z=500)) try: pr(func.__name__, func(10, 20, 300)) except TypeError as err: pr('with_asterisk_sentinel', err)
def exec(self): # -------------------------------------------- # venv で仮想環境を作り、 activate している状態だと # sys モジュールの prefixとbase_prefixの値が異なる # 状態となる。仮想環境を使っていない場合、同じ値となる. # -------------------------------------------- pr('prefix', sys.prefix) pr('exec_prefix', sys.exec_prefix) hr() pr('base_prefix', sys.base_prefix) pr('base_exec_prefix', sys.base_exec_prefix) pr('venv 利用している?', sys.prefix != sys.base_prefix)
def exec(self): ############################################## # dis モジュールは、pythonのバイトコードの # 解析をサポートしてくれるモジュール。 # # 大きく分けて2つの使い方がある # 1) dis.dis() # 2) dis.Bytecode() # # 1) は、指定された内容を逆アセンブルして出力してくれる。 # 引数の file に何も指定しない場合は標準出力に指定してくれる。 # # 2) は、python 3.4 で追加されたAPI。 # 指定の仕方は 1) とほぼ変わらないが、いきなり結果を # 出力ではなくて、一旦 Bytecode オブジェクトにラップして # 返してくれる。 # ############################################## listcomp_str = 'r = [x for x in range(1000000) if x % 2 == 0]' forloop_str = ''' r = [] for x in range(1000000): if x % 2 == 0: r.append(x) ''' ############################################### # dis.dis() ############################################### hr('dis.dis(listcomp_str)') dis.dis(listcomp_str) hr('dis.dis(forloop_str)') dis.dis(forloop_str) ############################################### # dis.Bytecode() # # python 3.4 から dis モジュールに追加されたAPI。 # 内部で code オブジェクトや dis.code_info() の # 結果を保持してくれたりするので、こちらの方が便利。 ############################################### hr('dis.Bytecode(listcomp_str)') listcomp_bytecode = dis.Bytecode(listcomp_str) print(listcomp_bytecode.codeobj) print(listcomp_bytecode.dis()) print(listcomp_bytecode.info()) hr('dis.Bytecode(forloop_str)') forloop_bytecode = dis.Bytecode(forloop_str) print(forloop_bytecode.codeobj) print(forloop_bytecode.dis()) print(forloop_bytecode.info())
def exec(self): # ---------------------------------------------------------------- # リストを コピー or クローン する場合、以下の選択肢がある # # (1) list.copy() を使う (Python 3.3 から追加) # (2) [:] のスライス # (3) list() を使う # (4) copy.copy() を使う # (5) copy.deepcopy() を使う # # ただし、 (5) 以外は、 「浅いコピー (shallow copy)」 となる. # 速度面からみると、当然 (5) が一番時間がかかる. # ---------------------------------------------------------------- a = Data(100) b = Data(200) original = [1, 'hello world', a, b] pr('orig', original) hr() # (1) no_1 = original.copy() # (2) no_2 = original[:] # (3) no_3 = list(original) # (4) no_4 = copy.copy(original) # (5) no_5 = copy.deepcopy(original) # 元の値を変更 original[0] = 999 original[1] = original[1].upper() a.val = 111 b.val = 222 pr('orig', original) hr() # 結果出力 pr('No.1', no_1) pr('No.2', no_2) pr('No.3', no_3) pr('No.4', no_4) pr('No.5', no_5)
def exec(self): # ---------------------------------------------- # BitArray には、指定したビット列を検索 # メソッドが存在する。 # # 以下の3種類がある。 # - find # - findall # - rfind # # find メソッドの結果は、1要素の tuple となる。 # findall メソッドは、generator となる。 # rfind メソッドの結果は、1要素の tuple となる。 # ---------------------------------------------- hr() ba01 = bs.BitArray('0x8F') pr('ba01.bin', ba01.bin) pr('ba01.uint', ba01.uint) hr('find 0xF') pos, = ba01.find('0xF') ba02 = ba01[pos:] pr('ba01.find', pos) pr('ba02.bin', ba02.bin) pr('ba02.uint', ba02.uint) hr('find 0b01') pos, = ba01.find('0b01') ba03 = ba01[pos:] pr('ba01.find', pos) pr('ba03.bin', ba03.bin) pr('ba03.uint', ba03.uint) hr('find 0b1111') pos, = ba01.find('0b1111') ba04 = ba01[pos:] pr('ba01.find', pos) pr('ba04.bin', ba04.bin) pr('ba04.uint', ba04.uint) hr('findall 0b1') gen_pos = ba01.findall('0b1') positions = list(gen_pos) pr('ba01.findall', type(gen_pos)) pr('ba01.positions', positions) pr('ba01.bit_values', [ba01[x] for x in positions])
def exec(self): # ----------------------------------------------- # itertools.cycle() # ---------------------- # itertools.cycleは、名前の通り # 最初に指定したシーケンスを繰り返しサイクルする # イテレータを生成してくれる。 # # これで生成したイテレータは無限にサイクルするので # ストップさせるのは、利用者側の責任になる。 # ------------------------------------------- numbers = list(range(0, 9)) cycle_iter = it.cycle((1, 2, 3)) hr('itertools.cycle()') for i, j in zip(numbers, cycle_iter): pr("i,j", f'{i}-{j}')
def exec(self): ############################################# # struct.pack メソッドについて # # struct.pack メソッドは指定されたフォーマットに従って # データをバイト列に変換してくれる。 # # 指定できる書式については以下を参照。 # https://bit.ly/2OQucjz # # 代表的なものは以下の通り。 (https://bit.ly/2AsLaeO) # < : リトルエンディアン # > : ビッグエンディアン # x : パディング # b : char # h : short # i : int # l : long # d : double # s : char[] # # どの項目も繰り返し回数を指定することができる。 # 例えば、 int が3つの場合は「3I」と指定できる。 # # たいていの項目は、大文字が unsigned を表す。 # # s フォーマットの場合のみ、繰り返し回数の指定は # バイト長として判断される。 # # # ------------------------------------------- # struct.unpack メソッドについて # # struct.unpack メソッドは pack メソッドの逆を行う。 ############################################# hr('fmt=3I values=(2, 1, 408)') fmt = '3I' vals = (2, 1, 408) b = struct.pack(fmt, *vals) print(b) hr('strcut.unpack') print(struct.unpack(fmt, b))
def exec(self): # ----------------------------------------------- # itertools.groupby() # ---------------------- # 指定された条件でグルーピングを行う。 # groupby() は、key で指定した関数が返す値が # 変わる度に新たなグループを生成するため # 通常指定するシーケンスはソート済みである必要がある。 # # また、groupby() が返すデータはそれ自身もイテレータ # となっているため、データが後の処理で必要な場合は # 別途リストなどを用意して保持しておく必要がある。 # (それか、再度 groupby() 呼ぶ) # ----------------------------------------------- GroupingData = collections.namedtuple('GroupingData', ['id', 'name']) # 未ソートのシーケンス groups_not_sorted = [ GroupingData(1, 'data1'), GroupingData(1, 'data2'), GroupingData(2, 'data3'), GroupingData(1, 'data4') ] def key_func(grp: GroupingData) -> int: return grp.id hr('未ソートの状態で groupby() ') grp_iter = it.groupby(groups_not_sorted, key=key_func) for k, g in grp_iter: pr('grp-key', k) pr('\tgrp-items', ','.join(_.name for _ in g)) hr('ソート済みの状態で groupby() ') # ソート済み groups_sorted = sorted(groups_not_sorted, key=key_func) grp_iter = it.groupby(groups_sorted, key=key_func) for k, g in grp_iter: pr('grp-key', k) pr('\tgrp-items', ','.join(_.name for _ in g))
def exec(self): ############################################# # datetime.fromtimestamp メソッドについて # # datetimeには、POSIX タイムスタンプを手軽に日時に変換できる # fromtimestamp # メソッドが存在する。このメソッドにタイムスタンプを渡すと # datetimeに変換してくれる. ############################################# hr('fromtimestamp(0)') try: print(datetime.fromtimestamp(0)) except OSError as e: # 呼び出し失敗は OSError が送出される print(e) hr('fromtimestamp(time.time())') now = time.time() print(datetime.fromtimestamp(now))
def exec(self): """サンプル処理を実行します""" # -------------------------------------------------------- # logging モジュールは、python 標準ライブラリで他の言語でいう # log4jやlog4netなどと同様にロギング処理を提供するもの。 # 公式ドキュメントでは以下のURLで説明が記載されている。 # # https://docs.python.jp/3/library/logging.html # # 非常に多機能であるため以下のチュートリアルが用意されている。 # # 基本:https://docs.python.jp/3/howto/logging.html#logging-basic-tutorial # 上級:https://docs.python.jp/3/howto/logging.html#logging-advanced-tutorial # # -------------------------------------------------------- # 今回も、 ハンドラ について # # loggingモジュールにおける # ハンドラの役目はログをどこに出力するか?という役割を持つ。 # # loggingモジュールは、デフォルトでいろいろな用途に利用できるハンドラを持っている。 # ハンドラの一覧については以下を参照。 # https://docs.python.jp/3/howto/logging.html#useful-handlers # -------------------------------------------------------- # StreamHandler # 最も基本的なハンドラ。ストリームを指定してログを出力することが出来る。 # デフォルトは、 sys.stderr となっている。 # loggingモジュールにてハンドラを設定ていない状態で # 利用すると自動的に追加されるのが、このハンドラ。 # -------------------------------------------------------- hr('StreamHandler') self._run_streamhandler_example() # -------------------------------------------------------- # FileHandler # 指定したファイルに出力するハンドラ。 # 出力機能は StreamHandler から継承している。 # -------------------------------------------------------- hr('FileHandler') self._run_filehandler_example()
def exec(self): # ----------------------------------------------- # itertools.repeat() # ---------------------- # itertools.repeat() は、指定したオブジェクトを # 指定した回数分繰り返すイテレータを生成してくれる。 # 繰り返し回数は、第二引数の times で指定できる。 # timesのデフォルトは None となっており、これは # 無限の繰り返しを表す。 # ----------------------------------------------- hr('it.repeat()') str01 = 'hello python' for x in it.repeat(str01, 2): pr('it-repeat', x) list01 = list(range(5)) for i, x in enumerate(it.repeat(list01)): # そのままだと、無限ループするので10回出力で止める if i >= 10: break pr('it-repeat times=None', f'{i} -- {x}')
def exec(self): # ----------------------------------------------- # itertools.islice() # ---------------------- # 指定されたイテレータブルに対してスライスを行う。 # islice() には、キーワード引数が存在せず位置引数のみ。 # なので、値の指定数で挙動が変わる。 # 戻り値も当然イテレータとなっている。 # # 引数が一つの場合、stopが指定された状態となる。 # 引数が二つの場合、start, stopが指定された状態となる。 # 引数が三つの場合、start, stop, stepが指定された(略 # ----------------------------------------------- hr('it.islice()') data01 = list(range(10)) pr('it.islice(8)', list(it.islice(data01, 8))) pr('it.islice(5,8)', list(it.islice(data01, 5, 8))) pr('it.islice(2,8,2)', list(it.islice(data01, 2, 8, 2))) # stop には、None が指定可能。None を指定した場合は最後までという意味となる。 pr('it.islice(5,None)', list(it.islice(data01, 5, None)))
def exec(self): """ 処理を実行します。 """ # ------------------------------------------------------------ # (1) サブクラス側が __init__ を定義していない場合 # 既定で、 super().__init__() が呼ばれた状態となる # ------------------------------------------------------------ obj1 = C() pr('C.x', obj1.x) hr() # ------------------------------------------------------------ # (2) サブクラス側が __init__ を定義しているが super().__init__() を # 呼んでいない場合、親クラスの __init__ は呼ばれない。 # # 他の言語に慣れている場合、デフォルトのコンストラクタが呼ばれるのが暗黙的なので # よく間違えてしまう。注意が必要。 # # PyCharmで作業している場合、IDE側が警告を出してくれるので気付ける。 # ------------------------------------------------------------ try: obj2 = D() pr('D.x', obj2.x) pr('D.y', obj2.y) except AttributeError as e: pr('D', e) hr() # ------------------------------------------------------------ # (3) サブクラス側が __init__ を定義していて super().__init__() を # 呼んでいる場合、親クラスの __init__ も呼ばれる。 # ------------------------------------------------------------ obj3 = E() pr('E.x', obj3.x) pr('E.y', obj3.y)
def exec(self): start_dt = NOW() # ---------------------------------------------------------- # joblibのParallel() は、CPU数とは別にいくつかのオプション引数 # を持つ。verboseもその一つで、値を指定することでjoblibの内部ログを # 出力することが出来る。値は、intとなっており、以下の値域を持つ。 # # [verboseの値域] # 0以外: 進捗ログを出力する (簡易) # 10以上: 進捗ログを出力する (各イテレーション毎に出力してくれる) # ---------------------------------------------------------- results = job.Parallel(n_jobs=CPU_COUNT, verbose=LOG_VERBOSE)([ job.delayed(heavy_proc)(f'value-{i}', RND.randrange(1, 3), True) for i in range(1, 5) ]) end_dt = NOW() pr('job-results', results) pr('total elapsed', (end_dt - start_dt).seconds) hr('log-verbose-all-iteration-report') start_dt = NOW() results = job.Parallel(n_jobs=CPU_COUNT, verbose=LOG_VERBOSE_ALL_ITERATION_REPORT)([ job.delayed(heavy_proc)(f'value-{i}', RND.randrange(1, 3), True) for i in range(1, 5) ]) end_dt = NOW() pr('job-results', results) pr('total elapsed', (end_dt - start_dt).seconds)
def exec(self): """サンプル処理を実行します""" ########################################################### # pathlib の基本的な使い方 # --------------------------------------------------------- # pathlib は、 3.4 から追加されたモジュール。 # これまで、 os.path などを利用して処理していたパス操作を専門に担当する # モジュールである。 # # pathlib モジュールで根幹となるアイテムは pathlib.Path となる。 # # pathlib は、純粋パスと具象パスの2種類の概念を扱う。 # クラス階層については、以下の公式ドキュメントを見るとわかりやすい。 # - https://docs.python.jp/3/library/pathlib.html # # 純粋パスは実際にOSに存在しているかどうかは関係なく処理できる。 # なので、OSにアクセスすることなく処理を確認したい場合などに利用できるらしい。 # (使ったことがありません。) # ########################################################### with tempfile.TemporaryDirectory() as tmpdir: # # (1) Path インスタンスの作成 # hr('Path.ctor()') tmp_dir = pathlib.Path(tmpdir) pr('tmp_dir', tmp_dir) # # (2) 存在確認は、exists メソッドで行う # hr('Path.exists()') pr('パス (tmp_dir)', str(tmp_dir)) pr('存在するか (/tmp)', tmp_dir.exists()) # # (3) パスの結合は、 / で行える # (4) HOME ディレクトリの取得は クラスメソッド home を呼び出す # hr('Path::home() and path concat by "/"') home_dir = pathlib.Path.home() work_dir = pathlib.Path('PyCharmProjects') py_dir = pathlib.Path('try-python') src_dir = home_dir / work_dir / py_dir pr('パス (src_dir)', str(src_dir)) pr(f'存在するか (src_dir)', src_dir.exists()) # # (5) Path 内のファイル一覧を取得するには iterdir メソッドを呼び出す # このメソッドは名前の通り イテレータ を返す # (6) ファイル名のみ(親ディレクトリを除く)を取得する場合は name プロパティを呼び出す # (7) ディレクトリかどうかは is_dir() を使う。ファイルかどうかは is_file() を使う。 # hr('Path.iterdir() and Path.is_dir() and Path.is_file()') pr('ファイル一覧 (iterdir)', src_dir.iterdir()) pr('ファイル一覧 (iterdir)', [x.name for x in src_dir.iterdir()]) pr('ファイル一覧 (is_dir)', [x.name for x in src_dir.iterdir() if x.is_dir()]) pr('ファイル一覧 (is_file)', [x.name for x in src_dir.iterdir() if x.is_file()]) # # (8) 現在のカレントディレクトリは、 Path.cwd() クラスメソッドで取得できる。 (os.getcwd() と同じ) # hr('Path::cwd()') pr('カレントディレクトリ (cwd)', pathlib.Path.cwd()) # # (9) シェルのように ~ を展開するには、 expanduser() メソッドを利用する。 # hr('Path.expanduser()') pr('~ の展開 (expanduser)', pathlib.Path('~/work').expanduser()) # # (10) ディレクトリの作成は mkdir(), 削除は rmdir() メソッドを利用する。 # 対象がファイルの場合、削除は unlink() メソッドを利用する。 # hr('Path.mkdir() and rmdir()') dir1 = src_dir / 'dir1' dir1.mkdir() pr('存在する (dir1)', dir1.exists()) dir1.rmdir() pr('存在する (dir1)', dir1.exists()) # # (11) Path は、ファイル操作用の API も備えている。 open メソッドでファイルをそのまま開ける。 # hr('Path.open()') readme = src_dir / 'README.md' with readme.open(encoding='utf-8') as fp: lines = list(fp) pr('README', f'{len(lines)} 行') # # (12) 単純にファイルの中身を全取得したい場合だったら, open メソッドを呼ぶ必要はなく # read_bytes() もしくは read_text() メソッドを呼び出せば取得できる。 # # (*) 書き込む場合は、 write_bytes(), write_text() を利用できる # hr('Path.read_text()') all_text = readme.read_text(encoding='utf-8') lines = all_text.split('\n')[:-1] pr('README (read_text)', f'{len(lines)} 行') # # (13) glob モジュールのように条件にマッチするファイルを抽出したい場合は glob() メソッドを利用する。 # glob() は、リストではなくジェネレータを返す。再帰的に抽出する場合は ** を利用する。 # # (*) Path には、 rglob() というメソッドもあるが、これは glob() に ** を指定した場合と同じ動作。 # hr('Path.glob()') all_py_files = src_dir.glob('**/*.py') top_10_files = it.islice(all_py_files, 10) top_10_names = [x.name for x in top_10_files] pr('glob (**/*.py)', top_10_names) hr('Path.rglob()') all_py_files = src_dir.rglob('*.py') top_10_files = it.islice(all_py_files, 10) top_10_names = [x.name for x in top_10_files] pr('rglob (*.py)', top_10_names)
def exec(self): # ----------------------------------------------- # itertools.product() # ---------------------- # デカルト積を求める。とドキュメントに記載されているが # 要は list01 と list02 が存在する場合 # (x, y) for x in list01 for y in list02 # と同じことになる。 # # キーワード引数の repeat を利用すると直積も求められる。 # ----------------------------------------------- hr('it.product()') list01 = list('ABC') list02 = list('DEF') pr('it.product(*iterable)', list(it.product(list01, list02))) pr('listcomp', [(x, y) for x in list01 for y in list02]) pr('it.product(*iterable, repeat=2)', list(it.product('AB', repeat=4))) # テストデータを一気に生成するときに便利 date_range = pd.date_range(dt.date.today(), periods=3, freq='D') values = (10, 11, 12) pr('it.product', list(it.product(date_range, values))) pr('listcomp', [(d, v) for d in date_range for v in values]) # 同じ # ----------------------------------------------- # itertools.permutations() # ---------------------- # 順列を生成する。(e.g. 5! = 5*4*3*2*1 = 120) # 第二引数に順列の長さを指定できる。省略可能 # 長さにNone(これがデフォルト)を指定すると # 可能な最長の順列が生成される。 # ----------------------------------------------- hr('it.permutations()') pr('it.permutations(r=None)...(3!=3*2*1)', list(it.permutations('ABC'))) # 長さを指定することで、シーケンスからいくつ選ぶのかを指定できる pr('it.permutations(r=3)...(4p3=4*3*2)', len(list(it.permutations('ABCD', r=3)))) pr('it.permutations(r=2)...(4p2=4*3)', len(list(it.permutations('ABCD', r=2)))) # ----------------------------------------------- # itertools.combinations() # ---------------------- # 組合せを生成する。 # 第二引数は必須。 # # 並べる際の順序を無視した結果を返す。 # つまり、(a, b, c)と(b, a, c)と(c, b, a)は同じとみなす。 # ----------------------------------------------- hr('it.combinations()') pr('it.combinations(r=2)...(4c2=4p2/2!)', list(it.combinations('ABCD', r=2))) pr('it.combinations(r=2)...(4c2=4p2/2!)', len(list(it.combinations('ABCD', r=2)))) # combinations_with_replacement() は、それぞれの要素が重複することを許す pr('it.combinations_with_replacement(r=2)', list(it.combinations_with_replacement('ABCD', r=2))) pr('it.combinations_with_replacement(r=2)', len(list(it.combinations_with_replacement('ABCD', r=2))))
def exec(self): # ----------------------------------------------- # itertools.starmap() # ---------------------- # グループ化済みの iterable に対して function を適用する。 # 例えば、zipした後の結果に対して、更に function 適用するなど。 # # つまり、以下のような感じ。 # # l1 = [1, 2, 3] # l2 = [9, 8, 7] # l3 = list(zip(l1, l2)) ==> [(1,9), (2,8), (3,7)] # # l3に対して operator.add で starmapする # list(itertools.starmap(operator.add, l3)) # # 結果は [10, 10, 10] となる。 # つまり、以下を実施したのと同じこと # # for item in l3: # operator.add(*item) # # なので、名前が starmap となっている # ----------------------------------------------- hr('it.starmap()') list01 = [9, 8, 7] list02 = [1, 2, 3] list03 = list(zip(list01, list02)) starmap = it.starmap(ope.sub, list03) pr('it.starmap', list(starmap)) list04 = list(zip(list01, list02, *list03)) pr('it.starmap', list(it.starmap(lambda *args: sum(args), list04))) # ----------------------------------------------- # itertools.takewhile() # ---------------------- # 指定した条件を満たす間、要素を返す。 # dropwhile() の 逆。 # # なので、一度でも条件から外れた場合、それ以降に # 条件を満たす値があっても要素は返らない。 # ----------------------------------------------- hr('it.takewhile()') list05 = sorted(it.chain(list01, list02)) pr('list05', list05) takewhile = it.takewhile(lambda x: x < 5, list05) pr('it.takewhile', list(takewhile)) # ----------------------------------------------- # itertools.tee() # ---------------------- # 指定された iterable を複数の独立した iterable にして返す。 # つまり、n=2 とすると、元の iterable を複製した # 二つの iterable が取得できる。(tuple(iterable, iterable)) # # 公式ドキュメントに記載されているように、一度 tee() を # 使用して分割した original iterable は、内部状態を共有しているので # もう別の場所では利用しないほうがいい。 # # 引用: # Once tee() has made a split, # the original iterable should not be used anywhere else; # otherwise, the iterable could get advanced # without the tee objects being informed. # ----------------------------------------------- hr('it.tee()') list06 = list('helloworld') it_tee = it.tee(list06, 2) it_asc, it_desc = it_tee[0], reversed(list(it_tee[-1])) for it01, it02 in zip(it_asc, it_desc): pr('it.tee', f'{it01}, {it02}')
def exec(self): ################################################################### # # 基本コンテナオブジェクトと読み取り専用オブジェクト # # Python の基本コンテナとして # * リスト (list) # * ディクショナリ (dict) # * セット (set) # が存在するが、それぞれ読み取り専用として扱えるオブジェクトが # 対で存在する。対応としては以下のようになる。 # # リスト # tuple に変更すると読み取り専用のリストとして使える # ディクショナリ # types.MappingProxyType オブジェクトが読み取り専用として使える # セット # frozenset が読み取り専用として使える # ################################################################## # # listとtuple # a_list = list(range(10)) pr('list', a_list) a_list.append(100) a_list.insert(0, 101) del a_list[1] pr('list is mutable', a_list) a_tuple = tuple(a_list) pr('tuple', a_tuple) try: # noinspection PyUnresolvedReferences a_tuple.append(102) except AttributeError as attr_ex: pr('tuple is immutable', attr_ex) try: # noinspection PyUnresolvedReferences del a_tuple[0] except TypeError as type_ex: pr('tuple is immutable2', type_ex) # tupleは生成時に元データをコピーして生成されるので # 元のデータに変更があっても影響はない a_list.clear() a_list.append(999) pr('tuple', a_tuple) hr() # # dictとtypes.MappingProxyType # a_dict = dict(hello=1, world=2) pr('dict', a_dict) a_dict['spam'] = 100 del a_dict['world'] pr('dict is mutable', a_dict) a_proxy = types.MappingProxyType(a_dict) try: # noinspection PyUnresolvedReferences a_proxy['hello'] = 200 except TypeError as type_ex: pr('MappingProxyType is immutable', type_ex) try: # noinspection PyUnresolvedReferences del a_proxy['hello'] except TypeError as type_ex: pr('MappingProxyType is immutable2', type_ex) # MappingProxyTypeのみの特殊動作。 # このオブジェクト自体が動的ビューなので # 元オブジェクトの変更は即座に反映される。 # (tupleやfrozensetの場合は元の値がコピーされているため元データ # 変更しても影響はない。) a_dict['world'] = 999 pr('proxy', a_proxy['world']) hr() # # setとfrozenset # a_set = set(a_tuple) pr('set', a_set) a_set.add(10) a_set.add(3) a_set.remove(2) pr('set is mutable', a_set) a_frozenset = frozenset(a_set) try: # noinspection PyUnresolvedReferences a_frozenset.add(4) except AttributeError as attr_ex: pr('frozenset is immutable', attr_ex) try: # noinspection PyUnresolvedReferences a_frozenset.remove(10) except AttributeError as attr_ex: pr('frozenset is immutable2', attr_ex) # frozensetは生成時に元データをコピーして生成されるので # 元のデータに変更があっても影響はない a_set.clear() a_set.add(999) pr('frozenset', a_frozenset) hr()
def exec(self): # ----------------------------------------------- # itertools.count() # ---------------------- # 無限イテレータ。 # 指定した start 値から step 分加算した値を # 生成し続ける。 # ----------------------------------------------- hr('itertools.count()') iter01 = it.count(start=10, step=2) for i in iter01: if i > 20: break pr('it.count', i) # ----------------------------------------------- # itertools.accumulate() # ---------------------- # 指定した関数の結果を累積した結果を返す。 # 汎用性が高い関数。seed は指定できない。 # 最終的な累積結果のみが欲しい場合はfunctools.reduce() を使う。 # # 操作するための関数を指定しない場合、デフォルトは加算(operator.add)となる # 操作関数は引数を二つ受け取るものを指定する必要がある # 一つ目の引数に現在の累積値、二つ目に次の項目が入る。 # 以下のようなイメージとなる # # list(itertools.accumlate([1, 2, 3, 4, 5])) # # operator.add(1, 2) ==> 3 # operator.add(3, 3) ==> 6 # operator.add(6, 4) ==> 10 # operator.add(10, 5) ==> 15 # # なので、結果は[1, 3, 6, 10, 15]となる # ----------------------------------------------- hr('itertools.accumulate()') data01 = [1, 2, 3, 4, 5] # 加算 pr('it.accumulate', list(it.accumulate(data01, op.add))) # 乗算 pr('it.accumulate', list(it.accumulate(data01, op.mul))) # 自前関数 def _accum(accum: int, curr: int) -> int: return accum * curr if accum < 20 else 20 pr('it.accumulate', list(it.accumulate(data01, func=_accum))) # ----------------------------------------------- # itertools.compress() # ---------------------- # 引数に指定されたシーケンスを第二引数で指定したリスト # 内でTrueとなっているもののみにフィルタリングする。 # 注意点として、zip関数と同様に短い方が終了した # 段階で処理が打ち切られる。 # ----------------------------------------------- hr('itertools.compress()') selector01 = [True if x >= 3 else False for x in data01] pr('it.compress', list(it.compress(data01, selectors=selector01))) # ----------------------------------------------- # itertools.dropwhile() # ---------------------- # 条件が成り立つ間、要素を捨てる。 # 条件が成り立たなくなってからの要素が返る。 # 他の言語では、SkipWhile() という名前になったりする。 # 第一引数が predicate つまり、条件。 # 第二引数が データ であることに注意。 # ----------------------------------------------- hr('itertools.dropwhile()') pr('it.dropwhile', list(it.dropwhile(lambda x: x < 3, data01))) # ----------------------------------------------- # itertools.filterfalse() # ---------------------- # ちょっと特殊な子で、条件がfalseとなるものが返る。 # predicate に None が指定できる。Noneを指定した # 場合は、データの要素が false 判定されたものが返る。 # 第一引数が predicate つまり、条件。 # 第二引数が データ であることに注意。 # ----------------------------------------------- hr('itertools.filterfalse()') pr('it.filterfalse', list(it.filterfalse(lambda x: x < 3, data01))) # ---------------------------------------------------------------------------- # 上の dropwhile と filterfalse では同じ条件を指定している。 # dropwhile は predicate が True の要素は飛ばして、falseになってから要素を返す。 # filterfalse は predicate が false の要素を返す。 # つまり、上の2つは対象データが[1, 2, 3, 4, 5]の場合は、同じ結果となる。 # # 違いが出るのは、一旦 True なデータが出現した後に 再度 false データが出現した場合 # # dropwhile() は、一度 True になった後は、以降のデータは無条件で全部返る。 # filterfalse() は、false の判定になったものしか返さない。 # ---------------------------------------------------------------------------- hr('difference between dropwhile() and filterfalse()') data02 = [*data01, 2, 1, 3, 4, 5] pr('it.dropwhile', list(it.dropwhile(lambda x: x < 3, data02))) pr('it.filterfalse', list(it.filterfalse(lambda x: x < 3, data02)))