概要

Fluent Python ―Pythonicな思考とコーディング手法
- 作者: Luciano Ramalho,豊沢聡,桑井博之,梶原玲子
- 出版社/メーカー: オライリージャパン
- 発売日: 2017/10/07
- メディア: 単行本(ソフトカバー)
- この商品を含むブログを見る
に書いてあった小ネタ。なるほどって思ったので忘れない内にメモ。
2つの数値を受け取る処理があって、中で処理して結果を返すときに
基本 python に任せておけば、いいようにやってくれるのですが
型を揃えたいときがあります。
たとえば、上に挙げた書籍「Fluent Python」のサンプルコードほぼパクリですが
以下のようなジェネレータ関数があるとして
def my_range1(begin, step, end=None): result = begin forever = end is None while forever or result < end: yield result result += step
以下のように使うと
gen1 = my_range1(1, 2, 5) for x in gen1: print(f'gen1={x}({type(x)})')
当然結果は
gen1=1(<class 'int'>) gen1=3(<class 'int'>)
となります。
でも、以下のように呼び出すと
gen1 = my_range1(1, 2.0, 5.0) for x in gen1: print(f'gen1={x}({type(x)})')
結果は
gen1=1(<class 'int'>) gen1=3.0(<class 'float'>)
ってなります。これは、beginが int で、step を float で指定しているため
最初の yield では、begin の値がそのまま生成されるため、int で、次からは step を加算した値が生成されるため float となります。
このままでいい場合もありますし、最初の yield の値から float で揃えたい場合もあります。
で、上の本に記載されていたのを使うと以下のようになります。
def my_range2(begin, step, end=None): result = type(begin + step)(begin) forever = end is None while forever or result < end: yield result result += step
変更点は、以下の部分です。
result = type(begin + step)(begin)
これ、最初「何やってるんだろう?」って思ったのですが、要は 一旦足してみてその結果の型で結果の初期値を生成ってやってるんですね。
なるほどぉって思いました。
で、このようにすると、結果が以下のようになります。
gen2 = my_range2(1, 2, 5) for x in gen2: print(f'gen2={x}({type(x)})')
結果は
gen2=1(<class 'int'>) gen2=3(<class 'int'>)
となり
gen2 = my_range2(1, 2.0, 5.0) for x in gen2: print(f'gen2={x}({type(x)})')
も
gen2=1.0(<class 'float'>) gen2=3.0(<class 'float'>)
と、一回目の yield から float になります。
ついでに、decimal.Decimal つかった場合も
gen1 = my_range1(1, Decimal(2.0), Decimal(5.0)) for x in gen1: print(f'gen1={x}({type(x)})') gen2 = my_range2(1, Decimal(2.0), Decimal(5.0)) for x in gen2: print(f'gen2={x}({type(x)})')
以下の結果となります。
gen1=1(<class 'int'>) gen1=3(<class 'decimal.Decimal'>) gen2=1(<class 'decimal.Decimal'>) gen2=3(<class 'decimal.Decimal'>)
サンプルコード
# coding: utf-8 from decimal import Decimal def my_range1(begin, step, end=None): result = begin forever = end is None while forever or result < end: yield result result += step def my_range2(begin, step, end=None): result = type(begin + step)(begin) forever = end is None while forever or result < end: yield result result += step if __name__ == '__main__': gen1 = my_range1(1, 2, 5) for x in gen1: print(f'gen1={x}({type(x)})') gen2 = my_range2(1, 2, 5) for x in gen2: print(f'gen2={x}({type(x)})') print('---------------------------------------') gen1 = my_range1(1, 2.0, 5.0) for x in gen1: print(f'gen1={x}({type(x)})') gen2 = my_range2(1, 2.0, 5.0) for x in gen2: print(f'gen2={x}({type(x)})') print('---------------------------------------') gen1 = my_range1(1, Decimal(2.0), Decimal(5.0)) for x in gen1: print(f'gen1={x}({type(x)})') gen2 = my_range2(1, Decimal(2.0), Decimal(5.0)) for x in gen2: print(f'gen2={x}({type(x)})')
結果は
gen1=1(<class 'int'>) gen1=3(<class 'int'>) gen2=1(<class 'int'>) gen2=3(<class 'int'>) --------------------------------------- gen1=1(<class 'int'>) gen1=3.0(<class 'float'>) gen2=1.0(<class 'float'>) gen2=3.0(<class 'float'>) --------------------------------------- gen1=1(<class 'int'>) gen1=3(<class 'decimal.Decimal'>) gen2=1(<class 'decimal.Decimal'>) gen2=3(<class 'decimal.Decimal'>)
となります。
python2 には、coerce って関数があったみたいですが廃止となったみたいですね。
以下も参考になりました。
過去の記事については、以下のページからご参照下さい。
- いろいろ備忘録日記まとめ
サンプルコードは、以下の場所で公開しています。
- いろいろ備忘録日記サンプルソース置き場