LoginSignup
0

More than 1 year has passed since last update.

Effective Python 学習備忘録 3日目 【3/100】

Last updated at Posted at 2021-01-04

はじめに

Twitterで一時期流行していた 100 Days Of Code なるものを先日知りました。本記事は、初学者である私が100日の学習を通してどの程度成長できるか記録を残すこと、アウトプットすることを目的とします。誤っている点、読みにくい点多々あると思います。ご指摘いただけると幸いです!

今回学習する教材

今日の進捗

  • 進行状況: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]

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0