LoginSignup
51
54

More than 5 years have passed since last update.

Python3.0からPython3.5での変更点

Last updated at Posted at 2015-12-15

はじめに

この記事は、Python その2 Advent Calendar 2015の16日目の記事です。

注意

今回は、

  • 新しいモジュール
  • 改良されたモジュール
  • 非推奨のモジュール
  • 最適化
  • IDLE
  • ビルドとC API
  • 内部のオブジェクトについてなどなど

については触れないこととし、Pythonの文法やプリミティブ型などの本質に近い部分のみの紹介にしようと思います。(幾つか触れている部分があるのでその場合は飛ばしてください・・)

最後のほう、時間がなくて雑になってますがお許しを・・・。

Python3.0 -> Python3.1

順序付き辞書

collections.OrderedDictクラスが導入されました。
挿入された順序を覚えている辞書、という感じです。

Python3.1
from collections import OrderedDict

>>> {2:"a", 1:"b", 3:"c"}.items()
dict_items([(1, 'b'), (2, 'a'), (3, 'c')])
>>> 
>>> OrderedDict([(2, "a"), (1, "b"), (3, "c")]).items()
ItemsView(OrderedDict([(2, 'a'), (1, 'b'), (3, 'c')]))

3桁区切りのための書式指定子

組み込み関数のformat()str.format()で3桁区切りのための書式指定子が使えるようになりました。
カンマで指定することができます。
int, float, complex, decimal.Decimalをサポートしています。

Python3.1
>>> format(123456789, ',d')
'123,456,789'
>>> format(123456789, ',f')
'123,456,789.000000'
>>> format(123456789, ',.2f')
'123,456,789.00'
>>> 
>>> "{0:,d}".format(123456789)
'123,456,789'
>>> "{0:,f}".format(123456789)
'123,456,789.000000'
>>> "{0:,.2f}".format(123456789)
'123,456,789.00'

いやー、恥ずかしながら書式の指定ができるのは初めて知りました。。
と、言いますがあるんだろうなとは思っていたけど調べてなかったやつですね。

format()の方はformat(value[, format_spec])のように書いてあるのでフォーマットの指定ができるのは分かるのですが、
str.format()の方はどこを見ればいいか全然わかりませんでしたね。組み込み型の方ではstr.format(*args, **kwargs)
としか書いてなくて全然わからないし。

とか言いつつなんとか見つけたのがここでした。(str.formatの真下にリンクがあったのは内緒。。。)

その他の言語変更

小さな変更についてです。簡単にまとめます。

ディレクトリとzipアーカイブのファイルを直接実行

__main__.pyを含むディレクトリとzipアーカイブは、インタプリタにその名前を渡すことで直接実行することができる。

うむ。わかりづらい。

__main__.py
print("Hello World!!")

こんなファイルを作り、python_test/__main__.pyというパスで保存する。
そして、zipを作ってみる。

$ zip -j python_test.zip python_test/__main__.py

__main__.pyがルートにあるか確認します。

$ unzip -l python_test.zip
Archive:  python_test.zip
  Length      Date    Time    Name
---------  ---------- -----   ----
       23  2015-12-16 12:34   __main__.py
---------                     -------
       23                     1 file

そしたら実行!

$ # フォルダから実行
$ python python_test
Hello World!!
$ # アーカイブから実行
$ python python_test.zip
Hello World!!

とまぁ、こんな感じで実行することができます。

int型にbit_lengthメソッドが追加

Python3.1
>>> n = 37
>>> bin(n)
'0b100101'
>>> n.bit_length()
6
>>> n = 2**123-1
>>> n.bit_length()
123

はい。こんな感じで2進数での桁数が出てきますね。

format()文字列のフィールドに自動的に番号が振られる。

Python3.1
>>> "{} {}!!".format("Hello", "World")
'Hello World!!'

これも初めて知りました・・・。
便利ですね〜。

string.maketrans()は非推奨に

新たにスタティックメソッドとして作成された

  • bytes.maketrans()
  • bytearray.maketrans()

を使用することができます。

with文のシンタックスは1つの文で複数のコンテキストマネージャーを渡せるように

Python3.1
>>> class WithTest:
...  def __init__(self, msg):
...   self.msg = msg
...  
...  def __enter__(self):
...   print("enter: "+self.msg)
...   return self
...  
...  def __exit__(self, exc_type, exc_value, traceback):
...   print("exit: "+self.msg)
...   return True
... 
>>> with WithTest("hoge") as hoge, WithTest("fuga") as fuga:
...  pass
... 
enter: hoge
enter: fuga
exit: fuga
exit: hoge

with文をネストさせずに一気に書くことができます。
動作としてはネストした動きと同じですね。

これによってcontextlib.nested()は非推奨になりました。

round(x, n)はxが整数の場合、整数を返すように

以前までは浮動小数点を返していたようです。

Python3.1
>>> round(1234, -2)
1200
>>> round(1234., -2)
1200.0
>>> round(1234, 2)
1234

整数を渡すと整数で返ってきていますね。

浮動小数点の表示にDavid Gayのアルゴリズムを使用

タイトルのままです。新アルゴリズムを使って浮動小数点を表示しています。

Python3.0
>>> 1.1
1.1000000000000001
Python3.1
>>> 1.1
1.1

これ以上の話は僕には難しかったです〜だれか教えてください!

Python3.1 -> Python3.2

argparseコマンドライン解析モジュール

optparseからのアップグレード版であるargparseモジュールが追加されました。

位置引数、サブコマンド、オプション検証などをサポートしている。

このモジュールについては公式が詳しいので詳しく知りたい方はこちらをどうぞ。

loggingの辞書ベースの設定

loggingモジュールの設定方法は関数を呼び出して設定するか、ConfigParserのフォーマットを利用したファイルを読み込む方法がありました。

この設定をフレキシブルに行える方法として辞書を利用した設定が行えるようになりました。logging.config.dictConfig()に設定の情報を
格納した辞書を渡すことで設定することができます。

JSONやYAMLで記述されたファイルをパースし、メソッドに渡すことでフレキシブルに設定が可能です。

loggingモジュールの詳細はこちら

concurrent.futuresモジュール

concurrent.futuresモジュールが追加されました。このモジュールは非同期に実行できる呼び出し可能オブジェクトの高レベルのインターフェースを提供します。

ThreadPoolExecutorを使ってスレッドで実行、またはProcessPoolExecutorを使って別のプロセスで実行することができます。

concurrent.futuresモジュールの詳細はこちら

PYCリポジトリーディレクトリ

Pythonが生成していたキャッシュファイルの.pycファイルは別バージョンのインタプリタで動作させる場合は既にあるキャッシュは利用できないため、新たにキャッシュファイルを作成し、上書きしてしまう。

これに対処するためにファイル名の後ろにPythonのバージョンを付け足したファイルを__pycache__ディレクトリにするようになりました。

Python3.2
$ touch mymodule.py
$ python -c "import mymodule"
$ ls
__pycache__  mymodule.py
$ ls __pycache__
mymodule.cpython-32.pyc

importしたモジュールのキャッシュが__pycache__フォルダの中にバージョン付きのファイル名で作成されていますね。

ABI バージョンでタグ付けされた .so ファイル

共有オブジェクトもPython実装方法(CPython, Jython, PyPyなど)+メジャーとマイナーバージョンの数字+ビルドフラグ
という名前を与えて複数のファイルを配置することができるようになりました。

Python Web Server Gateway Interface v1.0.1

このPEPではWSGIプロトコルによってbytesとtextの問題をどのように処理するかを明確にしています。
PEPはリクエストとレスポンスのボディーで使用されているバイト文字列に対して
リクエスト/レスポンスヘッダーとメタデータで使用されている、いわゆるネイティブ文字列を区別します。

ネイティブ文字列は常にstr型だが、Latin-1エンコーディングを使用してbytesに変換可能であり、U+0000からU+00FFの間のコードポイントに制限されています。
これらの文字列は、環境変数の辞書内のkeyとvalueと、start_response()のレスポンスヘッダーとステータスで使用されています。
つまり、ISO-8891-1の文字かRFC2047 MIMEエンコードを使用する必要があります。

CGI-WSGI経路のサーバー実装者やその他のCGIスタイルのプロトコルではユーザはプラットフォームが異なる場合でもネイティブ文字列を使用して環境変数にアクセスできるようにしなければなりません。

このギャップを埋めるため、wsgirefモジュールはos.environからCGIの変数をネイティブ文字列に変換し新たな辞書を返すwsgiref.handlers.read_environ()が新しく用意されました。

とまぁ、頑張って英語を読んでみたのですが、自分でも何が重要なのかあまりわかりませんでした(笑)

wsgiref.handlers.read_environ()が言いたかったにしては前置きが長いんですが。これだけだったんですかね・・・?

分かる方Help!!

その他の言語変更

format()str.format()の書式指定子に#が追加

  • 整数に対して2進・8進・16進の出力の場合、それぞれ'0b','0o','0x'の接頭辞を加えます
  • 浮動小数点数、複素数、10進数については小数点文字の後に数字がなくても小数点文字が含まれます
Python3.2
>>> format(10, 'o')
'12'
>>> format(10, '#o')
'0o12'
>>> format(10, 'b')
'1010'
>>> format(10, '#b')
'0b1010'
>>> format(10, '.0f')
'10'
>>> format(10, '#.0f')
'10.'

str.format_map()の追加

format、辞書バージョンって感じです。

Python3.2
>>> '{msg1} {msg2}!!'.format_map({"msg1":"Hello", "msg2":"World"})
'Hello World!!'

-qオプションの追加

Python3.2
$ python
Python 3.2.6 (default, Dec  9 2015, 17:42:33) 
[GCC 5.2.0] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> exit()
$ python -q
>>> 

-qオプションをつけることでインタプリタ起動時のバージョン情報などを表示しないようにすることが可能です。

hasattr()が実行時に例外を投げるようになった

hasattr()getattr()を呼び、例外が検知されるかを確認しています。
以前までの動きは何らかの例外が検知された場合はFalseになっていましたが、現在ではAttributeError以外の例外はスルーされます。

Python3.1
>>> class C:
...  @property
...  def f(self):
...   return 1//0
...  @property
...  def g(self):
...   return 0
... 
>>> c = C()
>>> hasattr(c, 'f')
False
>>> hasattr(c, 'g')
True
>>> hasattr(c, 'h')
False
Python3.2
>>> class C:
...  @property
...  def f(self):
...   return 1//0
...  @property
...  def g(self):
...   return 0
... 
>>> c = C()
>>> hasattr(c, 'f')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 4, in f
ZeroDivisionError: integer division or modulo by zero
>>> hasattr(c, 'g')
True
>>> hasattr(c, 'h')
False

Python3.1ではZeroDivisionErrorが出ていませんね。一見属性はあるように見えますが例外が出て結果がFalseになっています。

Python3.2では例外が出ていますね。しかし、AttributeErrorについては出ておらず結果がFalseになっています。

complexやfloatでのstr()の振る舞いがrepr()と同じになった

Python3.1
>>> import math
>>> repr(math.pi)
'3.141592653589793'
>>> str(math.pi)
'3.14159265359'
Python3.2
>>> import math
>>> repr(math.pi)
'3.141592653589793'
>>> str(math.pi)
'3.141592653589793'

3.1ではstr()の結果は少し短いですね。

memoryviewオブジェクトがrelease()メソッドを持つようになった

Python3.2
>>> with memoryview(b'abc') as v:
...  print(v.tolist())
...
[97, 98, 99]

release()メソッドを呼ぶことでリソースが開放されます。
さらに__enter__()__exit__()も実装されたのでwith文を使用することができます。

入れ子になったブロック内に自由変数として現れる名前を削除できるようになった

Python3.1
>>> def outer(x):
...  def inner():
...   return x
...  inner()
...  del x
...
SyntaxError: can not delete variable 'x' referenced in nested scope
>>> 
Python3.2
>>> def outer(x):
...  def inner():
...   return x
...  inner()
...  del x
...
>>> 

これ、何がいいんでしょうかね。
Error出してくれたほうが助かる気がするんですが。一応2.6のこういうようなコードが動くようです。

Python3.2
>>> def f():
...  def print_error():
...   print(e)
...  try:
...   pass
...  except Exception as e:
...   print_error()
>>> 

ここで知ったのが暗黙的にeがdelされるんですね。確かにこれは動いて欲しいですが、こんなコード書くんじゃないよとか思うの僕だけですかね?

内部のstructsequenceツールはタプルのサブクラスで作られるように

Python3.2
>>> isinstance(sys.version_info, tuple)
True

os.stat()time.gmtime()sys.version_infoで返されるそうです。
名前付きタプルのように扱えます。

PYTHONWARNINGS環境変数でWarningを簡単にコントロール

$ export PYTHONWARNINGS='ignore::RuntimeWarning::,once::UnicodeWarning::'

-Wオプションで指定することもできます。

ResourceWarningが追加

デフォルトでは無効になっているので表示するには-Wオプションで有効にする必要があります。

Python3.2
$ python -q -Wdefault
>>> f = open("foo", "wb")
>>> del f
__main__:1: ResourceWarning: unclosed file <_io.BufferedWriter name='foo'>

rangeオブジェクトでindexとcountとスライスがサポートされるように

Python3.2
>>> r=range(0,100,2)
range(0, 100, 2)
>>> r.count(10)
1
>>> r.index(10)
5
>>> r[10]
20
>>> r[0:5]
range(0, 10, 2)

sys.maxsizeより大きな値を持てるようですよ。

callable()組み込み関数が復活

Python3.2
>>> callable(max)
True
>>> callable(1)
False

呼び出し可能かチェックできる関数です。

モジュールのフォルダまでのパスにASCII文字以外も使えるように

なったそうです。

Python3.2 -> Python3.3

仮想環境について

プログラムからアクセスするためのvenvモジュールとコマンドラインからアクセスするためのpyvenvスクリプトが追加されました。

自分はvirtualenvしか使ったことなかったのですが、こっち使ったほうがいいのでしょうか?
それと、pyvenvって打つとusageにvenvって書いてあるんですけど。。

Python3.3
$ pyvenv
usage: venv [-h] [--system-site-packages] [--symlinks] [--clear] [--upgrade]
            ENV_DIR [ENV_DIR ...]
venv: error: the following arguments are required: ENV_DIR

このコマンド・モジュールはインタプリタコアとの統合による恩恵を受けているそうなので、こっち使ったほうがいいのかも。

暗黙的な名前空間パッケージ

パッケージディレクトリに__init__.pyを置く必要がなくなりました。

Python3.2
$ ls -R
hoge

./hoge:
fuga.py
$ python -c "import hoge.fuga"
Traceback (most recent call last):
  File "<string>", line 1, in <module>
ImportError: No module named hoge.fuga
Python3.3
$ ls -R
hoge

./hoge:
fuga.py
$ python -c "import hoge.fuga"
$ 

Python3.3ではエラーが出ていないので読み込めているようですね。

OS,IO例外階層の手直し

  • OSError
  • IOError
  • EnvironmentError
  • exc:WindowsError
  • mmap.error
  • socket.error
  • select.error

の例外の型はすべてOSErrorになりました。(他の名前はエイリアスとして残されている)

また、特定のエラー条件を補足するのが用意になりました。errno属性の特定の定数を調べる代わりに、適切なOSErrorの派生クラスを補足することができます。

  • BlockingIOError
  • ChildProcessError
  • ConnectionError
  • FileExistsError
  • FileNotFoundError
  • InterruptedError
  • IsADirectoryError
  • NotADirectoryError
  • PermissionError
  • ProcessLookupError
  • TimeoutError

ConnectionErrorには細かな派生クラスがあります。

  • BrokenPipeError
  • ConnectionAbortedError
  • ConnectionRefusedError
  • ConnectionResetError

公式の例ですが、

Python3.2
from errno import ENOENT, EACCES, EPERM

try:
    with open("document.txt") as f:
        content = f.read()
except IOError as err:
    if err.errno == ENOENT:
        print("document.txt file is missing")
    elif err.errno in (EACCES, EPERM):
        print("You are not allowed to read document.txt")
    else:
        raise
Python3.3
try:
    with open("document.txt") as f:
        content = f.read()
except FileNotFoundError:
    print("document.txt file is missing")
except PermissionError:
    print("You are not allowed to read document.txt")

と書けるようになりました。Python3.3、スマートですね!
Python3.2はC言語かよ・・・

サブジェネレータへの委譲構文

yield from文が追加されました。

ジェネレータは別のジェネレータにその処理を委譲することができます。

Python3.3
>>> def g():
...  yield from [1,2,3]
...  yield from [-1,-2,-3]
...
>>> gen = g()
>>> list(gen)
[1, 2, 3, -1, -2, -3]

また、yield from文はreturn文で指定された最終的な値を返すことができる。

Python3.3
>>> def g():
...  yield 1
...  yield 2
...  yield 3
...  return 0
...
>>> def f():
...  data = yield from g()
...  print('return value: ' + str(data))
...
>>> gen = f()
>>> next(gen)
1
>>> next(gen)
2
>>> next(gen)
3
>>> next(gen)
return value: 0
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration

例外コンテクストの抑制

raise from文でNoneを指定することで例外コンテクストの抑制ができます。

Python3.3
>>> def f():
...  raise Exception('in function')
...
>>> def g1():
...  try:
...   f()
...  except Exception as e:
...   raise Exception('test1')
...
>>> def g2():
...  try:
...   f()
...  except Exception as e:
...   raise Exception('test2') from None
...
>>> g1()
Traceback (most recent call last):
  File "<stdin>", line 3, in g1
  File "<stdin>", line 2, in f
Exception: in function

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 5, in g1
Exception: test1
>>> g2()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 5, in g2
Exception: test2

g2では例外コンテクストが抑制されていますね。

明示的なユニコードリテラル

文字列のプレフィックスにuが再びつけれるようになりました。

Python3.2
>>> u"test"
  File "<stdin>", line 1
    u"test"
          ^
SyntaxError: invalid syntax
Python3.3
>>> u"test"
'test'

クラスの関数の修飾名

__qualname__属性でトップレベルからのパスを得ることができます。

Python3.3
>>> class C:
...  class D:
...   def f():
...    pass
...
>>> C.D.__name__
'D'
>>> C.D.__qualname__
'C.D'
>>> C.D.f.__qualname__
'C.D.f'

組み込みの関数と型

  • open()に'x'モードが追加されました。ファイルが既に存在する場合は失敗します
  • print()にflushキーワード引数が追加されました。真の場合ストリームは強制的にフラッシュされます。
  • ハッシュのランダム化はデフォルトで有効になっています。object.__hash__()とPYTHONHASHSEEDを参照。
  • str型にcasefold()メソッドが追加された。

Python3.3 -> Python3.4

3.3から3.4では新しい文法機能は無いため短いです。

Python インストール時の PIP の明示的なブートストラッピング

これは標準でPIPが導入されるということでしょう。

新規作成されたファイル記述子は継承不可

だ、そうです。

しかし、必要な場合は以下のメソッドを使用することができます。

  • os.get_inheritable(), os.set_inheritable()
  • os.get_handle_inheritable(), os.set_handle_inheritable()
  • socket.socket.get_inheritable(), socket.socket.set_inheritable()

その他の言語の変更

  • min()max()はイテレートする要素がない場合キーワード引数で指定した値を返すことができます。
  • bytes.join()byte array.join()は引数に任意のバッファオブジェクトを受け入れます。
  • intコンストラクタは__index__()を持つ任意のオブジェクトを受け入れます。

Python3.4 -> Python3.5

3.5については自分があまり触っていないため浅〜くの紹介&時間がないため短く簡単に。。。

async、await構文の追加

コルーチン関数を作成するためにはasync def構文を使います。

Python3.5
>>> async def cofunc():
...  return 'test'
...

また、以下の構文が追加されています。

  • async for
  • async with

これらの構文はasync defで定義されたコルーチン関数の中でのみ使用できます。

コルイーチン関数の内部でawait文を使用することができます。
await文は結果の値が有効になるまでコルー人の実行をサスペンドすることができます。
任意のオブジェクトに__await__()メソッドを定義することで使用することができます。

行列乗算用の中置演算子

@を使用することで行列の乗算ができます。
オブジェクトに__matmul__()__rmatmul__()__imatmul__()実装することで使用することができます。

numpyで実装されているようです。公式docより。

Python3.5
>>> import numpy
>>> x = numpy.ones(3)
>>> x
array([ 1., 1., 1.])
>>> m = numpy.eye(3)
>>> m
array([[ 1., 0., 0.],
       [ 0., 1., 0.],
       [ 0., 0., 1.]])
>>> x @ m
array([ 1., 1., 1.])

Unpackingの拡張

イテレーターオブジェクトでは*、辞書では**をUnpackingを使用することができます。

Python3.5
>>> print(*[1,2], 3, *[4])
1 2 3 4
>>> def f(a, b, c, d):
...  print(a,b,c,d)
...
>>> f(**{'a': 1, 'd': 4}, **{'c': 3}, **{'b': 2})
1 2 3 4
>>> *range(4), 4
(0, 1, 2, 3, 4)
>>> [*range(4), 4]
[0, 1, 2, 3, 4]
>>> {*range(4), 4}
{0, 1, 2, 3, 4}
>>> {**{'x': 3}, 'y': 4}
{'x': 3, 'y': 4}

bytesとbytearrayでの%フォーマットのサポート

Python3.5
>>> b'test: %d' % 1
b'test: 1'

%bではUnicodeをサポートしていません。しかし、%aを使用することができます。

Python3.5
>>> b'Hello %b!' % b'World'
b'Hello World!'
>>> b'Hello %b!' % 'World'
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: %b requires bytes, or an object that implements __bytes__, not 'str'
>>> b'Hello %a!' % 'World'
b"Hello 'World'!"
>>> b'price: %a' % '10€'
b"price: '10\\u20ac'"

Type Hints

Type Hintsが導入されました。後日別記事にて詳細書きます。。

関数アノテーションを利用して指定します。

Python3.5
>>> def f(num: int) -> int:
...  return num+1
...

os.scandir()関数の追加

ディレクトリの中身をイテレートする関数です。早いそうですよ。皆さん使いましょう。

ls -a
.         ..        .hidefile empty     hoge
Python3.5
>>> import os
>>> for entry in os.scandir('.'):
...  if not entry.name.startswith('.') and entry.is_file():
...   print(entry.name)
...
empty

EINTRでのシステムコール時の再実行

以前までのPythonではInterruptedErrorを無視するか、キャッチ後再度スタートサせる機構を用意する必要がありました。

Python3.5からはEINTRでのシステムコールの場合、自動的に再実行がなされます。

ジェネレータ内でのStopIterationの扱いの変更

以前までは、ジェネレーター内でのStopIterationの発生はそのままレイズされます。

Python3.4
>>> def gen():
...  next(iter([]))
...  yield
...
>>> next(gen())
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 2, in gen
StopIteration

__future__generator_stopをインポートすることでRuntimeErrorとして例外が送出されます。

Python3.5
>>> from __future__ import generator_stop
>>> def gen():
...  next(iter([]))
...  yield
...
>>> next(gen())
Traceback (most recent call last):
  File "<stdin>", line 2, in gen
StopIteration

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
RuntimeError: generator raised StopIteration

おおよその平等性の比較

math.isclose()が追加されました。

Python3.5
>>> import math
>>> a = 5.
>>> b = 4.99998
>>> math.isclose(a, b, rel_tol=1e-5)
True
>>> math.isclose(a, b, rel_tol=1e-6)
False
>>> math.isclose(a, b, abs_tol=0.00003)
True
>>> math.isclose(a, b, abs_tol=0.00001)
False

終わりに

3.4、3.5がかなり雑な感じですが。。。

マイナーアップデートなので文法的なアップデートは少ないですね。
バグ修正・改良・モジュール追加を含めると鬼のような量になってしまうので今回は省きました。
が、3.4で追加されたasyncioモジュールや3.5で追加されたtypingモジュールなどは気になるので後に記事を書きたいと思います。

自分はPython3.3を触って、その後のバージョンの新機能についてはあまり触れていないため説明もあやふやな感じで申し訳ない(´・ω・`)

もし気になる方は公式のdocを見て下さいね!!(また丸投げ

明日は@fx-kirinさんです。

それでは良きPython3ライフを!!

51
54
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
51
54