Skip to content

Python豆知识

参考資料

1."import this"だけじゃない

import antigravity
import __hello__
Hello world!
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!

2. ...

...
Ellipsis
print(type(...))
print(id(...)) # ...はellipsisクラスのsingleton
print(id(...))
<class 'ellipsis'>
4352958848
4352958848
bool(...)
True

使い道

# passとして使う
def foo():
    pass

def bar():
    ...

foo()
bar()
# typingとして使う
from typing import Callable

def partial(func: Callable[..., str], *args) -> Callable[..., str]:
    # Body

PEP484

# Numpyでslice操作
import numpy as np

foo = np.arange(16).reshape(2, 2, 4)

foo
array([[[ 0,  1,  2,  3],
        [ 4,  5,  6,  7]],

       [[ 8,  9, 10, 11],
        [12, 13, 14, 15]]])
foo[:, :, 2]
array([[ 2,  6],
       [10, 14]])
foo[..., 2] # 同じ結果
array([[ 2,  6],
       [10, 14]])

3. shellのendを真似したいなら...(使わないでください)

__builtins__.end = None


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

print(my_abs(10))
print(my_abs(-10))
10
10

4. promptを自分で定義する

```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("サンジ、腹減った")
~
サンジ腹減った
サンジ腹減った
海賊王に俺はなる!>>>
## 5.and & or


```python
(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なら、最後の値を戻る
{}

6. リストを結合する

a = [1, 2]
b = [3, 4]
c = [5, 6]
sum((a, b, c), [])
[1, 2, 3, 4, 5, 6]
[*a, *b, *c]
[1, 2, 3, 4, 5, 6]
from itertools import chain

list(chain(a, b, c))
[1, 2, 3, 4, 5, 6]

7. [-5, 256]

a = -6
b = -6

c = -5
d = -5

print(a is b)
print(c is d)
False
True

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

8. 文字列のintern

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

9. dict()と{}

timeit -n 1000000 -r 5 dict()
112 ns ± 4.2 ns per loop (mean ± std. dev. of 5 runs, 1000000 loops each)
timeit -n 1000000 -r 5 {}
21.6 ns ± 1.42 ns per loop (mean ± std. dev. of 5 runs, 1000000 loops each)

{}dict()より早い

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

10."e"と"е"

valuе = 12
print(value)
---------------------------------------------------------------------------

NameError                                 Traceback (most recent call last)

<ipython-input-50-6b554beb183e> in <module>
      1 valuе = 12
----> 2 print(value)


NameError: name 'value' is not defined

原因は上の"valuе"の"е"は普通の"e"ではない

ord("e")
101
ord("е")
1077

使い道?
いたずら!

11. "site"

import sys

sys.path
['/Users/maozhongfu/GitHub/Python',
 '/Users/maozhongfu/.vscode/extensions/ms-python.python-2020.10.332292344/pythonFiles',
 '/Users/maozhongfu/.vscode/extensions/ms-python.python-2020.10.332292344/pythonFiles/lib/python',
 '/Library/Frameworks/Python.framework/Versions/3.8/lib/python38.zip',
 '/Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8',
 '/Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/lib-dynload',
 '',
 '/Users/maozhongfu/Library/Python/3.8/lib/python/site-packages',
 '/Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/site-packages',
 '/Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/site-packages/aeosa',
 '/Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/site-packages/IPython/extensions',
 '/Users/maozhongfu/.ipython',
 '/Users/maozhongfu/.vscode/extensions/ms-python.python-2020.10.332292344/pythonFiles/vscode_datascience_helpers/dataframes']
!python3 -m site
sys.path = [
    '/Users/maozhongfu/GitHub/Python',
    '/Users/maozhongfu/.vscode/extensions/ms-python.python-2020.10.332292344/pythonFiles',
    '/Users/maozhongfu/.vscode/extensions/ms-python.python-2020.10.332292344/pythonFiles/lib/python',
    '/Library/Frameworks/Python.framework/Versions/3.8/lib/python38.zip',
    '/Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8',
    '/Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/lib-dynload',
    '/Users/maozhongfu/Library/Python/3.8/lib/python/site-packages',
    '/Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/site-packages',
    '/Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/site-packages/aeosa',
]
USER_BASE: '/Users/maozhongfu/Library/Python/3.8' (exists)
USER_SITE: '/Users/maozhongfu/Library/Python/3.8/lib/python/site-packages' (exists)
ENABLE_USER_SITE: True

12. http server

cd <path> # シェアしたいフォルダまで移動
ipconfig # 自分のipを把握他人に教える
python3 -m http.server <port number> # port numberでサーバーを立ち上げる
# その後 "http://<ip adress>:<port number>"でアクセルできる

13. pydoc

python3 -m pydoc -p <port number>

14. ":=" (python3.8以上)

if..else..

age = 22
if age > 20:
    print("お酒を飲めてもいい")
お酒を飲めてもいい
if (age:=22) > 20:
    print("お酒を飲めてもいい")
お酒を飲めてもいい

while

file = open("demo.txt", "r")
while True:
    line = file.readline()
    if not line:
        break
    print(line.strip())
file = open("demo.txt", "r")
while (line := file.readline()):
    print(line.strip())

comprehension

scores = [22,54,75,89]
valid_scores = [
   longFunction(n)
   for n in scores
   if longFunction(n) # longFunctionは2回実行される
]
scores = [22,54,75,89]
valid_scores = [
   result for n in scores
   result := longFunction(n) # 一回だけ
]

15. decorator

🔗十个棒棒哒装饰器

パラメータありの関数のdecorator

def say_hello(country:str = None):
    def wrapper(func):
        def deco(*args, **kwargs):
            if country == "China":
                print("你好!")
            elif country == "Japan":
                print("こんにちは!")
            else:
                print("Winter is Coming!")

            func(*args, **kwargs)

        return deco
    return wrapper

@say_hello("China")
def 诸葛孔明():
    ...

@say_hello("Japan")
def 桃太郎():
    pass

@say_hello()
def JonSnow():
    pass
诸葛孔明()
桃太郎()
JonSnow()
你好!
こんにちは!
Winter is Coming!

パラメータ無しのクラスdecorator

class logger(object):
    def __init__(self, func):
        self._func = func

    def __call__(self, *args, **kwargs):
        print(f"[INFO]: function {self._func.__name__} is running")
        return self._func(*args, **kwargs)

@logger
def say(something):
    print(f"say {something}")

say("Hello")
[INFO]: function say is running
say Hello

パラメータありのクラスdecorator

class logger(object):
    def __init__(self, level="INFO"):
        self._level = level

    def __call__(self, func):
        def wrapper(*args, **kwargs):
            print(f"[{self._level}]: function {func.__name__} is running")
            func(*args, **kwargs)

        return wrapper

@logger(level="DEBUG")
def say(something):
    print(f"say {something}")

say("Hello") 
[DEBUG]: function say is running
say Hello

クラスのdecorator

def singleton(cls):
    _instance = {}

    def inner():
        if cls not in _instance:
            _instance[cls] = cls()
        return _instance[cls]
    return inner

@singleton
class Test(object):
    def __init__(self):
        pass

foo = Test()
bar = Test()
print(id(foo) == id(bar))
True

16.lru_cache

def fib(n):
    if n < 2:
        return n
    return fib(n - 2) + fib(n - 1)
from functools import lru_cache

@lru_cache()
def fib_with_cache(n):
    if n < 2:
        return n
    return fib_with_cache(n - 2) + fib_with_cache(n - 1)
time fib(20)
CPU times: user 2.65 ms, sys: 35 µs, total: 2.69 ms
Wall time: 2.75 ms





6765
time fib_with_cache(2000)
CPU times: user 1.35 ms, sys: 8 µs, total: 1.35 ms
Wall time: 1.38 ms





4224696333392304878706725602341482782579852840250681098010280137314308584370130707224123599639141511088446087538909603607640194711643596029271983312598737326253555802606991585915229492453904998722256795316982874482472992263901833716778060607011615497886719879858311468870876264597369086722884023654422295243347964480139515349562972087652656069529806499841977448720155612802665404554171717881930324025204312082516817125

17.print to file

with open("test.txt", mode="w") as f:
    print("hello world", file=f)
!cat test.txt
hello world
!rm test.txt

18. for..else.., try..else..

def check_item(source:list, target:str):
    for item in source:
        if item == target:
            print("あった!")
            break
    else:
        print("無かった")

source=["Apple", "Huawei", "Xiaomi"]
check_item(source, "Huawei")
あった!
check_item(source, "Sharp")
無かった
def test_try_else(attr1 = None):
    try:
        if attr1:
            pass
        else:
            raise
    except:
        print("Exception occurred...")
    else:
        print("No Exception occurred...")
test_try_else()
Exception occurred...
test_try_else("~")
No Exception occurred...

19.numba

!pip install numba
Collecting numba
  Downloading numba-0.51.2-cp38-cp38-macosx_10_14_x86_64.whl (2.2 MB)
     |████████████████████████████████| 2.2 MB 3.8 MB/s 
[?25hCollecting llvmlite<0.35,>=0.34.0.dev0
  Downloading llvmlite-0.34.0-cp38-cp38-macosx_10_9_x86_64.whl (18.4 MB)
     |████████████████████████████████| 18.4 MB 19.5 MB/s 
[?25hRequirement already satisfied: setuptools in /Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/site-packages (from numba) (50.3.2)
Requirement already satisfied: numpy>=1.15 in /Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/site-packages (from numba) (1.19.0)
Installing collected packages: llvmlite, numba
Successfully installed llvmlite-0.34.0 numba-0.51.2
import numba as nb

@nb.jit()
def nb_sum(a):
    Sum = 0
    for i in range(len(a)):
        Sum += a[i]
    return Sum

def py_sum(a):
    Sum = 0
    for i in range(len(a)):
        Sum += a[i]
    return Sum
import numpy as np
a = np.linspace(0,100,100)

%timeit np.sum(a) # numpy
%timeit sum(a) # pythonのsum関数
%timeit nb_sum(a) # numba
%timeit py_sum(a) # 上のやつ
4.1 µs ± 106 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
17.8 µs ± 193 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)
372 ns ± 0.67 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
23.8 µs ± 475 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)

20. 里程標日付

  • Python 1.0 1994
  • Python 2.Xの最期 2020/1/1
  • Python 3.0 2008/12/3

21.PEP

22.CPythonだけじゃない

23.拡張子

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

pythonw.exe or python.exe

24. Guido van Rossum(Python BDFL)

25.Position-only parameters(>=python 3.8)

import math

math.sin?
Signature: math.sin(x, /)
Docstring: Return the sine of x (measured in radians).
Type:      builtin_function_or_method

'/'前のパラメータは位置限定で、キーワードで指定するのはできない
'*'以後のパラメータはキーワード限定

import math

math.sin(x=1)
---------------------------------------------------------------------------

TypeError                                 Traceback (most recent call last)

<ipython-input-3-5993719a7116> in <module>
      1 import math
      2 
----> 3 math.sin(x=1)


TypeError: sin() takes no keyword arguments

26.typing

from typing import Tuple

RGB = Tuple[int, int, int]
HSL = Tuple[float, float, float]
def rgb_to_hsl(color: RGB) -> HSL:
def hsl_complement(color: HSL) -> HSL:
def hsl_to_rgb(color: HSL) -> RGB:
from typing import NamedTuple, Union

class RGB(NamedTuple):
    red: int
    green: int
    blue: int

def hex_to_rgb2(hx_int: Union[int, str]) -> RGB:
from typing import Optional, Tuple

def polydice(n: Optional[int] = None, sides: int = 6) -> Tuple[int, ...]:
    if n is None:
        n = 2 if sides == 6 else 1
    return tuple(random.randint(1, sides) for _ in range(n))

27.Exeを作る

exeファイルを作るにはいくつかのツールがある * PyInstaller -> Windows, MacOS対応, おすすめ * BriefCase -> Windows, MacOS対応 * py2exe * cx_freeze * Nuitka -> exeファイルを作る前にまずCコードに変換する

Windows

!pip install pyinstaller
Collecting pyinstaller
  Downloading pyinstaller-4.1.tar.gz (3.5 MB)
     |████████████████████████████████| 3.5 MB 7.8 MB/s 
[?25h  Installing build dependencies ... [?25ldone
[?25h  Getting requirements to build wheel ... [?25ldone
[?25h    Preparing wheel metadata ... [?25ldone
[?25hCollecting pyinstaller-hooks-contrib>=2020.6
  Downloading pyinstaller_hooks_contrib-2020.10-py2.py3-none-any.whl (166 kB)
     |████████████████████████████████| 166 kB 14.2 MB/s 
[?25hCollecting macholib>=1.8; sys_platform == "darwin"
  Downloading macholib-1.14-py2.py3-none-any.whl (37 kB)
Collecting altgraph
  Downloading altgraph-0.17-py2.py3-none-any.whl (21 kB)
Requirement already satisfied: setuptools in /Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/site-packages (from pyinstaller) (50.3.2)
Building wheels for collected packages: pyinstaller
  Building wheel for pyinstaller (PEP 517) ... [?25ldone
[?25h  Created wheel for pyinstaller: filename=pyinstaller-4.1-py3-none-any.whl size=2790248 sha256=b79a93485851db867f7f2f885330faa8e3d832ce0179768b80bc1b2f16e76d2f
  Stored in directory: /Users/maozhongfu/Library/Caches/pip/wheels/ae/7a/1e/e42202ec16f036e6c25592c6bc63d3c26e6a6addd6a25f053a
Successfully built pyinstaller
Installing collected packages: pyinstaller-hooks-contrib, altgraph, macholib, pyinstaller
Successfully installed altgraph-0.17 macholib-1.14 pyinstaller-4.1 pyinstaller-hooks-contrib-2020.10

commandline

%%writefile pysearch.py

import argparse
import pathlib


def search_folder(path, extension, file_size=None):
    """
    Search folder for files
    """
    folder = pathlib.Path(path)
    files = list(folder.rglob(f'*.{extension}'))

    if not files:
        print(f'No files found with {extension=}')
        return

    if file_size is not None:
        files = [f for f in files
                 if f.stat().st_size > file_size]

    print(f'{len(files)} *.{extension} files found:')
    for file_path in files:
        print(file_path)


def main():
    parser = argparse.ArgumentParser(
        'PySearch',
        description='PySearch - The Python Powered File Searcher')
    parser.add_argument('-p', '--path',
                        help='The path to search for files',
                        required=True,
                        dest='path')
    parser.add_argument('-e', '--ext',
                        help='The extension to search for',
                        required=True,
                        dest='extension')
    parser.add_argument('-s', '--size',
                        help='The file size to filter on in bytes',
                        type=int,
                        dest='size',
                        default=None)

    args = parser.parse_args()
    search_folder(args.path, args.extension, args.size)

if __name__ == '__main__':
    main()
Writing pysearch.py
!pyinstaller pysearch.py

GUI

%%writefile image_viewer.py

import wx

class ImagePanel(wx.Panel):

    def __init__(self, parent, image_size):
        super().__init__(parent)
        self.max_size = 240

        img = wx.Image(*image_size)
        self.image_ctrl = wx.StaticBitmap(self, 
                                          bitmap=wx.Bitmap(img))

        browse_btn = wx.Button(self, label='Browse')
        browse_btn.Bind(wx.EVT_BUTTON, self.on_browse)

        self.photo_txt = wx.TextCtrl(self, size=(200, -1))

        main_sizer = wx.BoxSizer(wx.VERTICAL)
        hsizer = wx.BoxSizer(wx.HORIZONTAL)

        main_sizer.Add(self.image_ctrl, 0, wx.ALL, 5)
        hsizer.Add(browse_btn, 0, wx.ALL, 5)
        hsizer.Add(self.photo_txt, 0, wx.ALL, 5)
        main_sizer.Add(hsizer, 0, wx.ALL, 5)

        self.SetSizer(main_sizer)
        main_sizer.Fit(parent)
        self.Layout()

    def on_browse(self, event):
        """
        Browse for an image file
        @param event: The event object
        """
        wildcard = "JPEG files (*.jpg)|*.jpg"
        with wx.FileDialog(None, "Choose a file",
                           wildcard=wildcard,
                           style=wx.ID_OPEN) as dialog:
            if dialog.ShowModal() == wx.ID_OK:
                self.photo_txt.SetValue(dialog.GetPath())
                self.load_image()

    def load_image(self):
        """
        Load the image and display it to the user
        """
        filepath = self.photo_txt.GetValue()
        img = wx.Image(filepath, wx.BITMAP_TYPE_ANY)

        # scale the image, preserving the aspect ratio
        W = img.GetWidth()
        H = img.GetHeight()
        if W > H:
            NewW = self.max_size
            NewH = self.max_size * H / W
        else:
            NewH = self.max_size
            NewW = self.max_size * W / H
        img = img.Scale(NewW,NewH)

        self.image_ctrl.SetBitmap(wx.Bitmap(img))
        self.Refresh()


class MainFrame(wx.Frame):

    def __init__(self):
        super().__init__(None, title='Image Viewer')
        panel = ImagePanel(self, image_size=(240,240))
        self.Show()


if __name__ == '__main__':
    app = wx.App(redirect=False)
    frame = MainFrame()
    app.MainLoop()
pyinstaller.exe image_viewer.py --noconsole

Mac

Macの場合は、選択肢は以下となる * PyInstaller * Briefcase * py2app

pyinstaller image_viewer.py --windowed

27. "_"と"__"

  • _: ループ、unpackingなどに利用されない臨時変数
  • _var: 直接アクセスしたくない
  • __var: private
  • var_: keywordとのコンフリクトを回避
  • __var__: magic functions