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

ネットをいろいろ見ていて、My Python Quizというサイトにたどり着いた *1

www.mypythonquiz.com

このサイトをなんて呼ぶのが良いのだろうか?
「テスト/試験」って書くと何かユニットテストの話に見えてしまうし。
「練習問題」って書くと「では素数判定を実装してください」みたいなものを想像しがちである。
「クイズ」と言うのが正しい気がする。
具体的な問題をQ&A形式で質問されるので、選択肢を選んで答えればよい。問題の具体的な内容は下にある。

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

20問まで解いてみて分からなかった問題を復習した。


Question #3:

def f(): pass
print(type(f()))

<class 'function'>じゃないのかと思ったけど違った。盛大に勘違いをしていた。

>>> def f():pass
...
>>> print(type(f()))
<class 'NoneType'>
>>> print(type(f))
<class 'function'>

fではなくてf()なので関数の戻り値の型を表示している。returnがないのでf()はNoneで、その型なのでNonetypeとなる。


Question #4: 
print(type(1J))

数値リテラルに 'j' または 'J' をつけると虚数 (実部がゼロの複素数) を与え、それに整数や浮動小数点数を加えて実部と虚部を持つ複素数を得られます。
4. 組み込み型 — Python 3.6.5 ドキュメント

というわけで複素数型で、クラスは<class 'complex'>です。


Question #5:
print(type(lambda:None))

Question #9:
x = 4.5
y = 2
print(x//y)

整数の除算を行うから出力は「2」かと思ったけど、正解は「2.0」。

Python は型混合の算術演算に完全に対応しています: ある二項算術演算子の被演算子の数値型が互いに異なるとき、より "制限された" 型の被演算子は他方の型に合わせて広げられます。ここで整数は浮動小数点数より制限されており、浮動小数点数複素数より制限されています。
整数の除算とも呼ばれます。結果の型は整数型とは限りませんが、結果の値は整数です。結果は常に負の無限大の方向に丸められます
4. 組み込み型 — Python 3.6.5 ドキュメント

浮動小数点として演算した後で、(数学の)floor関数(=床関数、ガウス記号の関数)を適用すれば良いようだ。
で、int < float < complexという型の「大小関係」があり、演算結果はより右側の型になる。それは整数除算であっても変わらない。だから今回の結果ではfloat型の2.0が出力される、ということね。


Question #11:
x = True
y = False
z = False

if x or y and z:
    print("yes")
else:
    print("no")
Question #12:
x = True
y = False
z = False

if not x or y:
    print(1)
elif not x or not y and z:
    print(2)
elif not x or y or not y and x:
    print(3)
else:
    print(4)

not演算子、and演算子、or演算子はこの順に優先順位が高く、先に評価される。


Question #13: If PYTHONPATH is set in the environment, which directories are searched for modules?
A) PYTHONPATH directory
B) current directory
C) home directory
D) installation dependent default path

ABCDの中から複数回答。

sys.path は以下の場所に初期化されます:

6. モジュール (module) — Python 3.6.5 ドキュメント

したがって、それぞれB,A,Dとなる。


Question #15:
print(r"\nwoow")

文字列リテラルとバイト列リテラルの両方は、任意で文字 'r' または 'R' をプレフィックスに持つことができます; そのような文字列は raw strings と呼ばれ、バックスラッシュをリテラル文字として扱います。
2. 字句解析 — Python 3.6.5 ドキュメント

rがプレフィックスに存在するので、raw stringとなり、バックスラッシュはそのまま表示される。その結果、\nwoowと表示される。


Question #16:
print("\x48\x49!")

\xhh 16進数値 hh を持つ文字
2. 字句解析 — Python 3.6.5 ドキュメント

この動作はC言語とよく似ている。


Question #17:
print(0xA + 0xa)

16進数の数値リテラルだろうとは想像がついたけど、16進数のまま出力されると勘違いして14(16進数)と答えてしまった。10進数で出力するので20(10進数)が正解である。


Question #18: What gets printed?
class parent:
    def __init__(self, param):
        self.v1 = param

class child(parent):
    def __init__(self, param):
        self.v2 = param

obj = child(11)
print(obj.v1 + " " + obj.v2)
>>> print(obj.v1 + " " + obj.v2)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'child' object has no attribute 'v1'

うっかりしてた。 parentクラスの__init__()を呼んでいないので、子クラスでは変数v1が定義されない。 C++だったらメンバ変数の定義はするから、親クラスのコンストラクタを通らなくても「メンバ変数が存在しない」という状況は発生しない。しかしPythonだと、こういう事態も起きるんだな。