pythonのクイズ、My Python Quizをやって復習した(その2)

前回に引き続いて、My Python Quizの21~40問目を解いて、分からなかった問題を復習した。

www.mypythonquiz.com

  • 選択肢の中から1つを選ぶ問題。
  • ほとんどの問題は「What gets printed?(以下のコードの結果、何が表示されるか)」という問題である。別の形式の場合はその都度説明する。 ただし選択肢の中に「実行時にエラーが発生する」が含まれる場合もある。

Question #27:
class Person:
    def __init__(self, id):
        self.id = id

obama = Person(100)

obama.__dict__['age'] = 49

print(obama.age + len(obama.__dict__))

object.__dict__
オブジェクトの (書き込み可能な) 属性を保存するために使われる辞書またはその他のマッピングオブジェクトです。
組み込み型 — Python 3.7.2 ドキュメント

見慣れない__dict__って何なんだ、と思ったら、オブジェクトの属性を辞書にして保持してたのね。下記のように属性が入るので、len(obama.__dict__)が2になるので答えは51だ。

>>> obama.__dict__
{'id': 100, 'age': 49}

Question #29: 
def simpleFunction():
    "This is a cool simple function that returns 1"
    return 1

print(simpleFunction.__doc__[10:14])

関数の説明を書いて、それを__doc__属性で呼び出せるよ、という話。
4. その他の制御フローツール — Python 3.7.2 ドキュメント あたりを参照。

公式のドキュメントにわかりやすい記述が見つからなかったので、以下のページをを参考に挙げる。
Pythonのdocstring(ドキュメンテーション文字列)の書き方 | note.nkmk.me


Question #30: What does the code below do?
sys.path.append('/root/mods')

sys.path
モジュールを検索するパスを示す文字列のリスト。 PYTHONPATH 環境変数と、インストール時に指定したデフォルトパスで初期化されます。
sys --- システムパラメータと関数 — Python 3.7.2 ドキュメント

sys.pathはリストなので、これに対してappendメソッドを呼ぶと要素を追加できる。新たにフォルダパスを追加すると、モジュールを検索するときにそのフォルダも探すようになる。


Question #33: True or false? Code indentation must be 4 spaces when creating a code block?
if error:
    # four spaces of indent are used to create the block
    print(msg)

そう言われたら空白4つじゃなきゃいけない気がしてきて、Trueを選んでしまった……(正解はFalse) Pythonのコーディング規約であるPEP8では、スペース4つを使うように書いてある。
しかし、言語の仕様としては別にスペースは4つでなくてもよい。

1レベルインデントするごとに、スペースを4つ使いましょう。
はじめに — pep8-ja 1.0 ドキュメント


Question #34: Assuming the filename for the code below is /usr/lib/python/person.py
and the program is run as: 
python /usr/lib/python/person.py 

What gets printed?
class Person:
    def __init__(self):
        pass

    def getAge(self):
        print(__name__)

p = Person()
p.getAge()

print(__name__)によって何が起きるか、という話。公式ドキュメントには上手くまとまった解説が見当たらなかったので、PyQから引用する。

  • import hello した:hello.py 内部で __name__ は "hello" という文字列になる
  • python hello.py した:hello.py 内部で __name__ は "__main__" という文字列になる

Pythonのif __name__ == "__main__" とは何ですか?への回答 - PyQオフィシャルブログ

設問の条件より、今回は後者に該当するので、print(__name__)とすると__main__という文字列が出力される。


Question #39: 
confusion = {}
confusion[1] = 1
confusion['1'] = 2
confusion[1.0] = 4

sum = 0
for k in confusion:
    sum += confusion[k]

print(sum)

このpython quizにたどり着いたきっかけとなった設問である。

もしふたつの数値が (例えば 1 と 1.0 のように) 等しければ、同じ辞書の項目として互換的に使用できます。 (ただし、コンピュータは浮動小数点数を近似値として保管するので、辞書型のキーとして使用するのはたいてい賢くありません。)
https://docs.python.org/ja/3/library/stdtypes.html#mapping-types-dict

さらにいうと、辞書のキーが一致するかを調べているのはhash()関数である。

hash(object) オブジェクトのハッシュ値を (存在すれば) 返します。ハッシュ値は整数です。これらは辞書を検索する際に辞書のキーを高速に比較するために使われます。等しい値となる数値は等しいハッシュ値を持ちます (1 と 1.0 のように型が異なっていてもです)。

これにより、1と1.0のハッシュ値は等しいので、キーとして一致する。

>>> 1 == 1.0
True
>>> hash(1) == hash(1.0)
True
>>> confusion
{1: 4, '1': 2}

Question #40: What gets printed?
boxes = {}
jars = {}
crates = {}

boxes['cereal'] = 1
boxes['candy'] = 2
jars['honey'] = 4
crates['boxes'] = boxes
crates['jars'] = jars

print(len(crates[boxes]))

何かややこしくてよく分からない問題だったけど、crates[boxes]crates['boxes']が別物だって分かってるかって話で良いのかな?
実際に打ち込んでみると以下のようにエラーが起きる。

>>> print(len(crates[boxes]))
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unhashable type: 'dict'

辞書のキーは ほぼ 任意の値です。ハッシュ可能 (hashable) でない値、つまり、リストや辞書その他のミュータブルな型 (オブジェクトの同一性ではなく値で比較されるもの) はキーとして使用できません
組み込み型 — Python 3.7.2 ドキュメント

  • 辞書のキーには、ハッシュ可能な値を使用することが可能、ハッシュ不可能な値を使用することはできないよ
  • 今回crates[boxes]って書いたけど、boxesは辞書型だからハッシュ不可能だよ
  • だからエラーが返るよ

という理屈だと思う。