Python 構文ベストプラクティス クラス以外

Pythonの組み込み型

文字列とバイト列

>>> print(bytes([102, 111, 111]))
b'foo'
>>>
>>> list(b'foo bar')
[102, 111, 111, 32, 98, 97, 114]
>>> tuple(b'foo bar')
(102, 111, 111, 32, 98, 97, 114)
>>>
>>> type("hoge moji")
<class 'str'>
>>> type(b"some bytes")
<class 'bytes'>

 

文字列の連結

>>> substrings=['hoge', 'piyo', 'foo', 'bar']
>>> "".join(substrings)
'hogepiyofoobar'
>>> ",".join(substrings)
'hoge,piyo,foo,bar'

 

リストの内包表記

下記のコードだと、C言語では問題なさそうだが、Pythonでは3つの問題がある。
・リストを操作するコードをループごとにインタープリタ上で処理する必要になる
・カウンタの操作もループごとにインタープリタ上で処理する必要になる
・append()はリストのメソッドであるため、
イテレーションごとに関数ルックアップの追加のコストが必要になる

>>> evens = []
>>> for i in range(10):
...     if i % 2 == 0:
...             evens.append(i)
...
>>> evens
[0, 2, 4, 6, 8]

下記のコードだと、処理の一部がインタープリタ内部で実行され、処理が早くなる。

>>> [i for i in range(10) if i % 2 == 0]
[0, 2, 4, 6, 8]

 

辞書の弱点と対応策

辞書はキーの順序を保持しない(python3.6前後で挙動が違うらしい)。
必ず、下記の実行結果になるとは限らないので、
順序を保持するなら、OrderedDictを利用する。

>>> {number: None for number in range(5)}.keys()
dict_keys([0, 1, 2, 3, 4])
>>>
>>> {str(number): None for number in range(5)}.keys()
dict_keys(['0', '1', '2', '3', '4'])
>>>
>>> from collections import OrderedDict
>>> OrderedDict((str(number), None) for number in range(5)).keys()
odict_keys(['0', '1', '2', '3', '4'])

 

Pythonの高度な文法

イテレータ, ジェネレータ, デコレータ, コンテキストマネージャについて

イテレータについて

イテレータはイテレータプロトコルを実装したコンテナオブジェクト
イテレータプロトコルは、下記2つのメソッドを持つ
・コンテナの次の要素を返す__next__()メソッド
・イテレータ自身を返す__iter__()メソッド

>>> i = iter('abcde')
>>> next(i)
'a'
>>> next(i)
'b'
>>> next(i)
'c'
>>> next(i)
'd'
>>> next(i)
'e'
>>> next(i)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration
# https://github.com/PacktPublishing/Expert-Python-Programming_Second-Edition/blob/master/chapter2/iterators.py

... class CountDown():
...     def __init__(self, step):
...         self.step = step
...     def __next__(self):
...         # コンテナの次の要素を返す
...         if self.step <= 0:
...             raise StopIteration
...         self.step -= 1
...         return self.step
...     def __iter__(self):
...         # イテレータ自身を返す
...         return self
...
>>> itr = CountDown(3)
>>> next(itr)
2
>>> next(itr)
1
>>> next(itr)
0
>>> next(itr)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 8, in __next__
StopIteration
>>> for element in CountDown(4):
...     print(element)
...
3
2
1
0

 

ジェネレータについて

ジェネレータは反復可能なオブジェクト
イテレータ同様forなどでループ処理を行うことができ、
その都度必要な分だけ値を生成させることができるため、
メモリを効率的に使用することができる。

# https://github.com/PacktPublishing/Expert-Python-Programming_Second-Edition/blob/master/chapter2/yield_fibonacci.py

>>> def fibonach():
...     a, b = 0, 1
...     while True:
...         yield b
...         a, b = b, a + b
...
>>> fib = fibonach()
>>> next(fib)
1
>>> next(fib)
1
>>> next(fib)
2
>>> next(fib)
3
>>> next(fib)
5
>>> next(fib)
8
>>> [next(fib) for i in range(10)]
[13, 21, 34, 55, 89, 144, 233, 377, 610, 987]

 

# https://github.com/PacktPublishing/Expert-Python-Programming_Second-Edition/blob/master/chapter2/yield_psychologist.py

>>> def psychologist():
...     print('あなたの悩みを聞かせてください')
...     while True:
...         answer = (yield)
...         if answer is not None:
...             if answer.endswith('?'):
...                 print("自分自身に問いかけをしすぎないようにしましょう")
...             elif '良い' in answer:
...                 print("それは良いですね。ぜひやりましょう")
...             elif '悪い' in answer:
...                 print("悲観的にならないようにしましょう")
...
>>> free = psychologist()
>>> next(free)
あなたの悩みを聞かせてください
>>> free.send('気分が悪いです')
悲観的にならないようにしましょう
>>> free.send('なぜ私はすべきではないんでしょうか?')
自分自身に問いかけをしすぎないようにしましょう
>>> free.send('なるほど。それなら何が私にとって良いかを探すべきですね')
それは良いですね。ぜひやりましょう

コメント

タイトルとURLをコピーしました