仕事にあまり役立たない豆知識

この記事に記載する豆知識は、素直に言うと仕事には使えなく役立たないものだと思います。  
が、知っておくと面白いかもしれません。

#  `import this`以外にも

Pythonに入門する際、[PEP20](https://www.python.org/dev/peps/pep-0020/)の`The Zen of Python(Pythonの禅)`を聞いたことがあって試したことがあると思います。

```
>>> import this
The Zen of Python, by Tim Peters

Beautiful is better than ugly.
Explicit is better than implicit.
Simple is better than complex.
Complex is better than complicated.
Flat is better than nested.
Sparse is better than dense.
Readability counts.
Special cases aren't special enough to break the rules.
Although practicality beats purity.
Errors should never pass silently.
Unless explicitly silenced.
In the face of ambiguity, refuse the temptation to guess.
There should be one-- and preferably only one --obvious way to do it.
Although that way may not be obvious at first unless you're Dutch.
Now is better than never.
Although never is often better than *right* now.
If the implementation is hard to explain, it's a bad idea.
If the implementation is easy to explain, it may be a good idea.
Namespaces are one honking great idea -- let's do more of those!
```

実は、これ以外にもあります。

## `import __hello__`

pythonは他のプログラミング言語に比べて、**Hello World!**　の出力方法は簡単です。

```python
print("Hello World!")
```

`print()`以外に、プロンプト環境にて`import __hello__`を入力したら同じ出力が出てきます。

```
>>> import __hello__
Hello world!
```

なお、Python 2.7の環境で実行すると、句読点が若干違います。

```
Python 2.7.18 (default, Oct  2 2021, 04:20:38) 
[GCC Apple LLVM 13.0.0 (clang-1300.0.29.1) [+internal-os, ptrauth-isa=deploymen on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import __hello__
Hello world...
```

余談：`__hello__`は`frozen module`のテストケースとして存在しています。
> Python 2.xのソースコード： https://github.com/python/cpython/blob/main/Tools/freeze/hello.py  
> Python 3.xのソースコード： https://github.com/python/cpython/blob/main/Tools/freeze/flag.py  
> stackoverflow上の参考記事： https://stackoverflow.com/questions/44333381/how-does-import-hello-work

## `import antigravity`

プロンプト環境にて`import antigravity`を実行すると、Pythonに関する面白い漫画サイトに遷移します。

```
>>> import antigravity
```

サイト：　https://xkcd.com/353/

一番最初出てくる漫画は、先ほどの`import`文を説明しています。：
![`import antigravity`-comic.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/667650/bd4f54b9-24d8-d115-37fb-8bfe5f56b6aa.png)

対話の内容：  
上：  
"YOU'RE FLYING!HOW?"  
"PYTHON!"  
左下：  
"I LEARNED IT LAST NIGHT! EVERYTHING IS SO SIMPLE!"  
" HELLO WORLD IS JUST `print "hello, world!"` "  
真ん中下：  
"I DUNNO...DYNAMIC TYPING?WHITESPACE?"  
"COME JOIN US!PROGRAMMING IS FUN AGAIN!IT'S A WHOLE NEW WORLD UP HERE!"  
"BUT HOW ARE YOU FLYING?"  
右下：  
"I JUST TYPED `import antigravity`"  
"THAT'S IT?"  
"...I ALSO SAMPLED EVERYTHING IN THE MEDICINE CABINET FOR COMPARISON."  
"BUT I THINK THIS IS THE PYTHON."  

訳（参考として...）:  
上：  
”あなたが飛んでいる！どうやって？”  
”Python！”  
左下  
”私は昨晩Pythonを勉強した！何でもかんでも簡単だ！”  
”Hello Worldを出力には`print "hello, world"`だけでいい！”　＃ Python 2の`print`は関数ではなくexpressionである  
真ん中下：  
”私はまだわかっていない...動的型？ホワイトスペース？”  
”我々に参加しよう！プログラミングは再び面白くなった！ここには斬新な世界がある！”  
”だが、どうやった飛んだ？”  
右下：  
”さっき`import antigravity`を入力した”  
”それだけ？”  
”比較対象として、薬品収納庫中の全てをサンプルした”  
”でも、原因はPythonだと思う”  

# `...`



Ellipsisオブジェクト（`...`）については下記の記事が既にあります。

https://qiita.com/yubessy/items/cc1ca4dbc3161f84285e



２点だけ補足させて頂きます。

1. `...`はellipsisクラスの**singleton**

   ```
   >>> ...
   Ellipsis
   >>> Ellipsis
   Ellipsis
   >>> type(...)
   <class 'ellipsis'>
   >>> id(...)
   4341386976
   >>> id(...)
   4341386976
   ```

   

2. [FastAPI](https://fastapi.tiangolo.com/ja/)には、[`...`を必須引数として使う](https://fastapi.tiangolo.com/ja/tutorial/query-params-str-validations/#_6)

# Shell,Rubyのように`end`を記述する

Pythonはインデントを利用して構造を表示しますが、ShellやRubyのように`end`を書くこともできます。

```python
__builtins__.end = None


def my_abs(x):
    if x > 0:
        return x
    else:
        return -x
    end
end

print(my_abs(10)) # 10
print(my_abs(-10)) # 10
```

勿論、本番環境にはこれは画蛇添足ですね。

# プロンプトの文字を変える

Pythonのプロンプトは`>>>`を利用しています。実は、これを変えることができます。

```
>>> import sys
>>> sys.ps1
'>>> '
>>> sys.ps2
'... '
>>> sys.ps3
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: module 'sys' has no attribute 'ps3'
>>> sys.ps1 = "海賊王に俺はなる!>>>"
海賊王に俺はなる!>>>sys.ps2 = "肉~"
海賊王に俺はなる!>>>for i in range(2):
肉~     print("サンジ、腹減った")
肉~
サンジ、腹減った
サンジ、腹減った
海賊王に俺はなる!>>>
```

このように、`sys.ps1`と`sys.ps2`を上書きすることで、自分でプロンプト文字を設定できます。

※ ここの`ps`はPowerShellの略ではなく、`prompt string`の頭文字です。

# `and`と`or`の戻り値

JavaScriptを書く人たちはよく`||`の戻り値を利用しています。

例：

```javascript
app.set('port', process.env.PORT || 3000);
```

Pythonの場合、利用する場面結構少ないためあまり知らないかもしれませんが、実は`and`と`or`には戻り値があります。

```
>>> (2 or 3) * (5 and 6 and 7)
14
>>> "" and "b" # andは左から右へスキャンして、最初のFalseを戻る
''
>>> "a" and "b" # 全てTrueなら、最後の値を戻る
'b'
>>> "" or "a" # orは左から右へスキャンして、最初のTrueを戻る
'a'
>>> "" or [] or None or {} # 全てFalseなら、最後の値を戻る
{}
```



# `.py`以外の拡張子

- `.py` ->　ソースコード
- `.pyc` -> バイトコンパイル済ファイル
- `.pyo` -> 最適化(-O)されたバイトコード
- `.pyd` -> Pythonスクリプトから*Windows DLL*を作ったもの
- `.py3` -> Python3 スクリプト
- `.pyw` -> Windows用のPythonスクリプトファイル, `pythonw.exe`で実行
- `.pyx` -> ソースを*C/C++*へコンバートしたもの

# [-5, 256]間の整数と文字列のintern

ご存知の通り、`is`演算子はCPythonの環境において、オブジェクトの一意の番号を取得する`id()`関数を利用して同じオブジェクトであるかを判断します。

そこで、以下のような"不思議"があります。

```
>>> a = -6
>>> b = -6
>>> a is b
False
>>> c = -5
>>> d = -5
>>> c is d
True
```

なぜでしょうか？

> CPythonでは、[-5, 256]間の整数は他の数字と違って、事前に定義されたオブジェクトである

似たようなCPythonの文字列の具体的な実現あります：[string interning](https://www.codesansar.com/python-programming/string-interning.htm)

```
>>> s1 = 'hello'
>>> s2 = 'hello'
>>> s1 is s2 # internと言うのは同じ文字列はメモリにただ一つ保存され
True
>>> s3 = 'hel lo'
>>> s4 = 'hel lo'
>>> s3 is s4 # スペースある場合は、internは起動しない
False
```

# `dict()`と`{}`

空の辞書オブジェクトの作成方法は、`dict()`と`{}`の２つです。

`dis.dis()`を利用してバイトコードを解析しましょう。



```
>>> from dis import dis
>>> dis('{}')
  1           0 BUILD_MAP                0
              2 RETURN_VALUE
>>> dis('dict()')
  1           0 LOAD_NAME                0 (dict)
              2 CALL_FUNCTION            0
              4 RETURN_VALUE
```

`dis()`関数にしては以下の記事が参考になります。

https://qiita.com/tans/items/4547db36150e67b13bc5



ご覧の通り、`dict()`の実行は`dict`の名前をロード->関数を実行->値を戻る三ステップがあります。

一方、`{}`は直接オブジェクトを作成して戻ります。

このため、`{}`のスピードは若干早いです。

```
>>> import timeit
>>> sum(timeit.repeat("dict()", number=10_000_000))/5
0.612105483599953
>>> sum(timeit.repeat("{}", number=10_000_000))/5
0.21801867480007786
```



# 参考記事

https://stackoverflow.com/questions/101268/hidden-features-of-python