はじめに
Twitterで一時期流行していた 100 Days Of Code なるものを先日知りました。本記事は、初学者である私が100日の学習を通してどの程度成長できるか記録を残すこと、アウトプットすることを目的とします。誤っている点、読みにくい点多々あると思います。ご指摘いただけると幸いです!
今回学習する教材
-
Effective Python
- 8章構成
- 本章216ページ
今日の進捗
- 進行状況:29-37ページ
- 本日学んだことの中で、よく忘れるところ、知らなかったところを書いていきます。
None を返すよりは例外を選ぶ
python では、None, 0, 空文字列, などがすべて条件式においてFalseと評価されてしまう。そのため、戻り値にNoneを使ってしまうと、思わぬ問題を引き起こしてしまう可能性がある。boolメソッドを使って、確認してみる。
a = 0
b = []
c = {}
d = None
e = ''
bool(a)
# False
bool(b)
# False
bool(c)
# False
bool(d)
# False
bool(e)
# False
Noneを使ったときに起こる問題の一例として、ある数を別の数で割るヘルパー関数を考える。ゼロで割る場合には、結果が未定義なので、Noneを返すように定義。
def divide(a, b):
try:
return a / b
except ZeroDivisionError:
return None
a / b の結果がゼロの場合を考える。この時、Noneも0も共に、条件式ではFalseと判定されてしまうため、以下の条件だと誤った結果になってしまう。
a, b = 0, 5
result = divide(a, b):
if not result:
print('Invalid inputs') # 間違い
Noneをそもそも返さず、例外を呼び出し元にあげて、その処理をさせることがこの問題の解決策。
def divide(a, b):
try:
return a / b
except ZeroDivisionError as e:
raise ValueError('Invalid inputs') from e
関数が例外を上げなければ、戻り値は問題ないはずと判断することができる。
クロージャが変数スコープとどう関わるかを知っておく
- クロージャとは、定義されたスコープの変数を参照する関数のこと
- クロージャ関数は、定義されたスコープのどこからでも変数を参照できる
- デフォルトでクロージャは変数代入で、周囲のスコープには影響できない
- python3ではnonlocal分を用いてクロージャが外のスコープにある変数を修正できる
- nonlocal分を単純な関数でのみ使うようにする
スコープの範囲について確認する
def scope1(x):
number = x
def scope2(x):
number = 1
scope2(number)
return number
number = 0
x = scope1(number)
print(x)
# 0
scope2でnumberに1を代入したが、戻り値は0になっている。この原因は、スコープが異なるためでである。scope1内のnumberと、scope2内のnumberは別の変数として処理されているため、値が変わっていない。異なるスコープの変数を横断するために、nonlocalを使って次のように定義する。
def scope1(x):
number = x
print(x)
def scope2(x):
nonlocal number
number = 1
scope2(number)
return number
number = 0
x = scope1(number)
print(x)
# 1
リストを返さずにジェネレータを返すことを考える
- ジェネレータを使うと、格納した結果をリストで返すよりもコードが明確になる
- ジェネレータが返すイテレータは、ジェネレータ関数の本体でyield式に渡される一連の値を生成する
- ジェネレータでは、作業メモリにすべての入出力を保持する必要が無いので、どのような長さの入力に対しても出力のシーケンスを生成できる
リストで返すコード
def index_words(text):
result = []
if text:
result.append(0)
for index, letter in enumerate(text):
if letter == ' ':
result.append(index + 1)
return result
words = 'Hello Effective Python'
result = index_words(words)
print(result)
出力結果
[0, 6, 16]
ジェネレータで返すコード
def index_words(text):
result = []
if text:
yield 0
for index, letter in enumerate(text):
if letter == ' ':
yield index + 1
return result
# ジェネレータ呼び出しで返されるジェネレータは、組み込み関数listでlistに変換できる
result = list(index_words(words))
print(result)
出力結果
[0, 6, 16]