top
おさだのホームページ

Python3.10が便利な話
2021年5月3日に Python 3.10.0 beta 1 がリリースされ、今後3つのベータ版がリリースされることが公表されている。 今回は pyenv でインストールできる一番新しいバージョンである Python 3.10.0 alpha 5 でできる機能を紹介する。


0. まずインストール

インストール方法は Python のバージョンを切り替える方法と変わらない。

copy
 
$ pyenv install --list

を実行しインストール可能なバージョンを確認する。
3.10.0a5 が一番最新であったのでそれをインストールする。

copy
 
$ pyenv install 3.10.0a5
$ pyenv local 3.10.0a5

一応インストールされたバージョンを確認する。

copy
 
$ python --version

>> Python 3.10.0a5

そして Python のソースコードの出力でも確認してみる。

copy
 
import sys
print(sys.version)

>> 3.10.0a5 (default, May 17 2021, 21:46:58) \n[Clang 12.0.5 (clang-1205.0.22.9)]

しっかり Python 3.10 がインストールされている。


1. 複数の型を指定する方法が簡単になった

これまで、複数の型を指定するときは typing モジュールを使用する必要があった。
例えば変数 x に int 型と float 型の両方を指定したい時、

copy
 
from typing import TypeVar
MyType = TypeVar("MyType", int, float)
x : MyType

#または

from typing import Union
MyType = Union[int, float]
x : MyType

とする必要があったが、3.10 からは

copy
 
x : int | float

これで済むようになった。

ちなみに

copy
 
print( type(int | float) )

>> <class "types.Union">

になる。
そしてこれを利用することでアノテーションと、複数の型との比較が簡単になる。


1-1. アノテーションとしての利用

例えば関数の引数に int か float 型を受け取りたい時、

copy
 
def func(x:int | float):
    return x**2

このように書ける。この場合「x は int または float にしてくれ」とアノテーションを入れることができる。

更に関数の戻り値にも書けるので、

copy
 
def func(x:int | float) -> int | float:
    return x**2

とすることで「引数は int または float、戻り値も int または float」とまで表現できる。
一応 func() の引数を確認してみると

copy
 
import inspect

def check(func):
    def f(*args, **kwargs):
        sig = inspect.signature(func)
        for (name, param) in sig.parameters.items():
            print(f"引数: {name}\n型: {repr(param.annotation)}")
        print(f"結果: {func(*args, **kwargs)}")
        return func
    return f

@check
def func(x:int | float) -> int | float:
    return x**2

func(2.0)

実行結果↓

copy
 
引数 : x
型 : int | float
結果 : 4.0

引数 x のアノテーションが int または float 型になっていることが確認できる。


1-2. 複数の型との比較

これまでの書き方で変数 x が int または float 型であることを確認するためには、

copy
 
if((type(x) is int) or (type(x) is float)):
    print("ok!")

    # または

if(isinstance(x, int) or isinstance(x, float)):
    print("ok!")

などと書く必要があったが、型を複数指定できるようになったので、

copy
 
if(isinstance(x, int | float)):
    print("ok!")

このように簡単に書けるようになった。


2. 辞書にmapping属性が追加された

mapping は辞書の keys() 、values() 、items() メソッドの後ろに keys().mapping と付けることでもとの辞書全体を参照することができる。

具体的には、

copy
 
# もとの辞書
x = {1:"a", 2:"b"}
print(f"dectionaly : {x}")

# そのキー
key = x.keys()
print(f"key : {list(key)}")

# キー.mappnig
key_mapping = key.mapping
print(f"key.mapping : {key_mapping}")

実行結果↓

copy
 
dictionaly : {0: "a", 1: "b"}
key : [0, 1]
key.mapping : {0: "a", 1: "b"}

このようにもとの辞書が復元されている。クラスの内と外のようなスコープの違う場所にある辞書全体を持ってきたいときに使えそうだ。

3. int.bit_count() で2進数の1の数が簡単にわかる

まず動作を見てもらうと、

copy
 
int.bit_count(1)  # 1
>> 1

int.bit_count(2)  # 10
>> 2

int.bit_count(15)  # 1111
>> 4

int.bit_count(1000)  # 1111101000
>> 6

このように2進数にした際の1の数が返される。

1の数を返すアルゴリズムはそんなに難しくないが、この bit_count() はパッケージ化され動作が速い。 では実際に bit_count() と自作の1を数える処理をそれぞれ100万回実行し、時間を比べてみる。


自作の処理1
copy
 
func1 = lambda x : len([i for i in str(bin(x))[2:] if i is 1])

処理2
copy
 
func2 = lambda x : len([i for i in str(bin(x))[2:] if not i])

処理3
copy
 
def func3(x):
    x -= (x >> 1) & 0x5555555555555555
    x = (x & 0x3333333333333333) + ((x >> 2) & 0x3333333333333333)
    x = (x + (x >> 4)) & 0x0f0f0f0f0f0f0f0f
    x += x >> 8
    x += x >> 16
    x += x >> 32
    return x & 0x0000007f

処理4
copy
 
func4 = lambda x : str(bin(x))[2:].count(1)


比較結果↓

mandelblot^2の画像

このように結構速い。


このアルゴリズムはインターネット通信のパリティによく使われるので、もしかしたらその方向へのアプローチかもしれない。


4. 2021年10月には match 文が実装される

10月にリリース予定の Python 3.10.0 final には match 文が含まれることがわかっている。match 文は他の言語でいう switch 文のことである。
もし実装されたら、

copy
 
match x:
    case 0:
        print("zero")
    case 1:
        print("one")
    case 2:
        print("two")
    case _:
        print("default")

このような処理が可能になる。

楽しみ!!!!!