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


この記事に記載する豆知識は、素直に言うと仕事には使えなく役立たないものだと思います。

が、知っておくと面白いかもしれません。

import this以外にも

Pythonに入門する際、PEP20The 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! の出力方法は簡単です。

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文を説明しています。:

対話の内容:

上:

"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オブジェクト(...)については下記の記事が既にあります。

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

  1. ...はellipsisクラスのsingleton
   >>> ...
   Ellipsis
   >>> Ellipsis
   Ellipsis
   >>> type(...)
   <class 'ellipsis'>
   >>> id(...)
   4341386976
   >>> id(...)
   4341386976
  1. FastAPIには、...を必須引数として使う

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

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

__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.ps1sys.ps2を上書きすることで、自分でプロンプト文字を設定できます。

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

andorの戻り値

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

例:

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

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

>>> (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

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

dict(){}

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

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()関数にしては以下の記事が参考になります。

ご覧の通り、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

参考記事