🦁

Pythonチュートリアルで学んだ中で個人的に大事だと思う部分をピックアップしてみた

2024/11/26に公開

はじめに

Pythonを業務で触る上で体系的に学び直したいと思い、公式チュートリアルを学ぼうと思いました。
その中でも、自分の中で大事と思ったところをピックアップして記事にしてみました。※学んだことがあったら随時更新(したい。。。)

4. その他の制御フローツール

4.9.6. ラムダ式

https://docs.python.org/ja/3/tutorial/controlflow.html#lambda-expressions

ラムダ式を使うと小さなラムダ関数を作成可能。

設定した値を足し引き

>>> def make_incrementor(n):
...     return lambda x: x + n
...
>>> f = make_incrementor(42)
>>>
>>> print(f(-1))
41

make_incrementor関数は、引数nを取り、lambda x: x + nというラムダ関数を返します。
このラムダ関数は、引数xを取り、x + nの結果を返します。
make_incrementor(n)を呼び出すと、xnを加える関数が生成され、それを返します。

リスト各要素を2倍に

>>> numbers = [1, 2, 3, 4, 5]
>>> doubled = list(map(lambda x: x * 2, numbers))
>>> print(doubled)
[2, 4, 6, 8, 10]

mapとリスト内包表記
map構文

map(function, iterable[, iterable2, ...])

function...関数やラムダ式など
iterable...リストやタプルなど

mapを使った場合

numbers = [1, 2, 3, 4]
result = map(lambda x: x * 2, numbers)
print(list(result))  # [2, 4, 6, 8]

リスト内包表記を使った場合

numbers = [1, 2, 3, 4]
result = [x * 2 for x in numbers]
print(result)  # [2, 4, 6, 8]

5. データ構造

https://docs.python.org/ja/3/tutorial/datastructures.html

appendextendの違い

a = [1, 2, 3]
a.append([4, 5, 6])  # appendを使用
print(a)  # 出力: [1, 2, 3, [4, 5, 6]]

a = [1, 2, 3]
a.extend([4, 5, 6])  # extendを使用
print(a)  # 出力: [1, 2, 3, 4, 5, 6]

5.1.3 リスト内包表記

https://docs.python.org/ja/3/tutorial/datastructures.html#list-comprehensions

平方のリストを作成する

通常の書き方

>>> squares = []
>>> for x in range(10):
...     squares.append(x**2)
...
>>> squares
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

リスト内包表記の書き方

squares = [x**2 for x in range(10)]

リスト内包表記は括弧の中の式、for、そして0個以上のforかifで構成される

下記の式はどちらも等価である

[(x, y) for x in [1,2,3] for y in [3,1,4] if x != y]
[(1, 3), (1, 4), (2, 3), (2, 1), (2, 4), (3, 1), (3, 4)]
combs = []
for x in [1,2,3]:
    for y in [3,1,4]:
        if x != y:
            combs.append((x, y))

combs
[(1, 3), (1, 4), (2, 3), (2, 1), (2, 4), (3, 1), (3, 4)]

5.1.4. ネストしたリストの内包表記

https://docs.python.org/ja/3/tutorial/datastructures.html#nested-list-comprehensions

長さ4のリスト3つからなる、3x4 の matrix について考える

matrix = [
    [1, 2, 3, 4],
    [5, 6, 7, 8],
    [9, 10, 11, 12],
]

下のリスト内包表記は、matrix の行と列を入れ替える

[[row[i] for row in matrix] for i in range(4)]
[[1, 5, 9], [2, 6, 10], [3, 7, 11], [4, 8, 12]]

下記コードと等価になる

transposed = []
for i in range(4):
    # the following 3 lines implement the nested listcomp
    transposed_row = []
    for row in matrix:
        transposed_row.append(row[i])
    transposed.append(transposed_row)

transposed

備考...zip関数

各リストの対応するインデックスの要素を合わせてタプル化

>>> for item in zip([1, 2, 3], ['sugar', 'spice', 'everything nice']):
...     print(item)
...
(1, 'sugar')
(2, 'spice')
(3, 'everything nice')
>>> list(zip(range(3), ['fee', 'fi', 'fo', 'fum']))
[(0, 'fee'), (1, 'fi'), (2, 'fo')]

5.2 del

https://docs.python.org/ja/3/tutorial/datastructures.html#the-del-statement

リストのインデックスを指定して値を削除する
返り値はない


>>> a = [-1, 1, 66.25, 333, 333, 1234.5]
>>> del a[0]
>>> a
[1, 66.25, 333, 333, 1234.5]
>>>
>>> del a[2:4]
>>> a
[1, 66.25, 1234.5]
>>>
>>> del a[:]
>>> a
[]

5.5. 辞書型 (dictionary)

https://docs.python.org/ja/3/tutorial/datastructures.html#dictionaries

dict()コンストラクタ

dict([('sape', 4139), ('guido', 4127), ('jack', 4098)])
{'sape': 4139, 'guido': 4127, 'jack': 4098}

リスト内包表記を使って辞書を作成可能

{x: x**2 for x in (2, 4, 6)}
{2: 4, 4: 16, 6: 36}

キーワード引数

dict(sape=4139, guido=4127, jack=4098)
{'sape': 4139, 'guido': 4127, 'jack': 4098}

5.6. ループのテクニック(items)

https://docs.python.org/ja/3/tutorial/datastructures.html#looping-techniques

辞書に対しitems()メソッドを使うとキーとそれに対応するため値を取り出せる

knights = {'gallahad': 'the pure', 'robin': 'the brave'}
for k, v in knights.items():
    print(k, v)

gallahad the pure
robin the brave

配列に対しenumerate()を使うと要素とインデックスを取り出せる

for i, v in enumerate(['tic', 'tac', 'toe']):
    print(i, v)

0 tic
1 tac
2 toe

シーケンスを逆方向に渡ってループするには、まずシーケンスの範囲を順方向に指定し、reversed()を使う。

for i in reversed(range(1, 10, 2)):
    print(i)

9
7
5
3
1

6. モジュール

https://docs.python.org/ja/3/tutorial/modules.html

以下解説用のモジュールファイル

fibo.py

def fib(n):    # write Fibonacci series up to n
    a, b = 0, 1
    while a < n:
        print(a, end=' ')
        a, b = b, a+b
    print()

def fib2(n):   # return Fibonacci series up to n
    result = []
    a, b = 0, 1
    while a < n:
        result.append(a)
        a, b = b, a+b
    return result

6.1. モジュールについてもうすこし

https://docs.python.org/ja/3/tutorial/modules.html#more-on-modules

asまたはfromを使うことでモジュールを任意の名前で扱えるようになる

as

import fibo as fib
fib.fib(500)
0 1 1 2 3 5 8 13 21 34 55 89 144 233 377

from

from fibo import fib as fibonacci
fibonacci(500)
0 1 1 2 3 5 8 13 21 34 55 89 144 233 377

6.11 モジュールをスクリプトとして実行する

https://docs.python.org/ja/3/tutorial/modules.html#executing-modules-as-scripts

モジュールを以下のように実行するとモジュール内のコードが実行できる

$ python fibo.py <arguments>

モジュールの末尾に以下を追加することで、同時にスクリプトとして実行できるようになる

if __name__ == "__main__":
    import sys
    fib(int(sys.argv[1]))
$ python fibo.py 50
0 1 1 2 3 5 8 13 21 34

モジュールが import された場合は、そのコードは実行されない

import fibo

7. 入力と出力

https://docs.python.org/ja/3/tutorial/inputoutput.html#input-and-output

7.1.2. 文字列の format() メソッド

https://docs.python.org/ja/3/tutorial/inputoutput.html#the-string-format-method

str.format()

{}を引数の値に置き換えられる

print('We are the {} who say "{}!"'.format('knights', 'Ni'))
We are the knights who say "Ni!"
print('{0} and {1}'.format('spam', 'eggs'))
spam and eggs
print('{1} and {0}'.format('spam', 'eggs'))
eggs and spam

備考...*,**可変長引数の使い方(*args,**kwsrgs)

*(*args)

引数を可変長にできる。

def my_sum2(*args):
    print('args: ', args)
    print('type: ', type(args))
    print('sum : ', sum(args))

my_sum2(1, 2, 3, 4)
# args:  (1, 2, 3, 4)
# type:  <class 'tuple'>
# sum :  10

引数は全てタプルに格納される。よってargsにインデックスでアクセス可能

def show_args(*args):
    print(args[0])  # 最初の引数を表示

show_args(10, 20, 30)  # 10

**(**kwargs)

複数のキーワード引数を辞書として受け取る

def func_kwargs(**kwargs):
    print('kwargs: ', kwargs)
    print('type: ', type(kwargs))

func_kwargs(key1=1, key2=2, key3=3)
# kwargs:  {'key1': 1, 'key2': 2, 'key3': 3}
# type:  <class 'dict'>

引数はキーとセットで辞書へ格納される。その辞書へアクセスして値を取得できる。

def show_kwargs(**kwargs):
    print(kwargs.get('key1'))  # 'key1'の値を表示

show_kwargs(key1=10, key2=20)  # 10

ファイルを読み書きする(with)

https://docs.python.org/ja/3/tutorial/inputoutput.html#reading-and-writing-files

以下公式ドキュメントより

open() は file object を返します。大抵、 open(filename, mode, encoding=None) のように2つの位置引数と1つのキーワード引数を伴って呼び出されます。

f = open('workfile', 'w', encoding="utf-8")

ファイルオブジェクトを扱うときに with キーワードを使うのは良い習慣です。 その利点は、処理中に例外が発生しても必ず最後にファイルをちゃんと閉じることです。 with を使うと、同じことを try-finally ブロックを使って書くよりずっと簡潔に書けます:
withを使うと自動でcloseが行われる

with open('workfile', encoding="utf-8") as f:
    read_data = f.read()

# We can check that the file has been automatically closed.
f.closed
True

7.2.2. json による構造化されたデータの保存

https://docs.python.org/ja/3/tutorial/inputoutput.html#saving-structured-data-with-json

文字列表現からデータを再構築することは、デシリアライズ (deserializing) と呼ばれます。シリアライズされてからデシリアライズされるまでの間に、オブジェクトの文字列表現はファイルやデータの形で保存したり、ネットワークを通じて離れたマシンに送ったりすることができます。

json.dumps()

import json
x = [1, 'simple', 'list']
json.dumps(x)
'[1, "simple", "list"]'

8. エラーと例外

https://docs.python.org/ja/3/tutorial/errors.html

8.3. 例外を処理する

https://docs.python.org/ja/3/tutorial/errors.html#handling-exceptions

while True:
    try:
        x = int(input("Please enter a number: "))
        break
    except ValueError:
        print("Oops!  That was no valid number.  Try again...")

もしもexpect内にて指定された例外と一致しないエラーが発生すると、その例外はtryの外側に渡される。外に対するハンドラ (handler、処理部) がどこにもなければ、 処理されない例外 (unhandled exception) となり、エラーメッセージを出して実行を停止する。

try..expectはオプションとしてelseを設けることができる。elseを設ける場合、全てのecpectより後ろに記述しなくてはならない。
elsetryにて全く例外が送出されなかったおきに実行できるコードとして役立つ。

for arg in sys.argv[1:]:
    try:
        f = open(arg, 'r')
    except OSError:
        print('cannot open', arg)
    else:
        print(arg, 'has', len(f.readlines()), 'lines')
        f.close()

例外ハンドラは、try 節 の直接内側で発生した例外を処理するだけではなく、そのtry節 から (たとえ間接的にでも) 呼び出された関数の内部で発生した例外も処理する。
以下例

def this_fails():
    x = 1/0

try:
    this_fails()
except ZeroDivisionError as err:
    print('Handling run-time error:', err)

Handling run-time error: division by zero

8.4. 例外を送出する(raise)

https://docs.python.org/ja/3/tutorial/errors.html#raising-exceptions

raise文を使って例外を発生させることが可能。

raise NameError('HiThere')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
    raise NameError('HiThere')
NameError: HiThere

raiseの唯一の引数は創出される例外を示す。
これは例外インスタンスや例外クラス(BaseException を継承したクラス、たとえば Exception やそのサブクラス)でなければならない。

8.5. 例外の連鎖

https://docs.python.org/ja/3/tutorial/errors.html#exception-chaining

ある例外から直接影響されていることを示すためにraiseのオプションのfromを指定する。

raise RuntimeError from exc
def func():
    raise ConnectionError

try:
    func()
except ConnectionError as exc:
    raise RuntimeError('Failed to open database') from exc

Traceback (most recent call last):
  File "<stdin>", line 2, in <module>
    func()
    ~~~~^^
  File "<stdin>", line 2, in func
ConnectionError

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

Traceback (most recent call last):
  File "<stdin>", line 4, in <module>
    raise RuntimeError('Failed to open database') from exc
RuntimeError:

8.7. クリーンアップ動作を定義するfinally

https://docs.python.org/ja/3/tutorial/errors.html#predefined-clean-up-actions

try文にはクリーンアップ動作がある。どんな状況でも必ず実行される

>>> try:
...     raise KeyboardInterrupt
... finally:
...     print('Goodbye, world!')
...
Goodbye, world!
Traceback (most recent call last):
  File "<stdin>", line 2, in <module>
KeyboardInterrupt
>>>

もしfinallyがある場合、try文が終わる前の最後の処理をfinallyが実行する。
tryが例外を発生させるかに問わずfinally実行される。

複雑なケース

  • もしtryの実行中に例外が発生したらexpectによって処理される。もし例外がexpectにより処理されなければfinallyが実行された後にその例外が再送出される。
  • expectまたはelseにて例外が発生したとする。その場合はfinallyが実行された後に例外が再送出される。
  • finally内でbreak,continue,returnが実行された場合、例外は再創出されない。
  • もしtry文がbreak,continueまたはreturnのいずれかに達するとそのbreak,continueまたはreturnの直前にfinallyが実行される。
  • もしfinallyreturnを含む場合、返される値はtryreturnではなくfinallyreturnになる

def divide(x, y):
    try:
        result = x / y
    except ZeroDivisionError:
        print("division by zero!")
    else:
        print("result is", result)
    finally:
        print("executing finally clause")

divide(2, 1)
result is 2.0
executing finally clause
divide(2, 0)
division by zero!
executing finally clause
divide("2", "1")
executing finally clause
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
    divide("2", "1")
    ~~~~~~^^^^^^^^^^
  File "<stdin>", line 3, in divide
    result = x / y
             ~~^~~
TypeError: unsupported operand type(s) for /: 'str' and 'str'

上記よりfinallyはどの場合でも実行される。
一番最後の文字列の割り算でTypeErrorexpectで処理されないため、先にfinallyが実行されたのちに例外が再創出される。

8.9. 複数の関連しない例外の送出と処理

いくつか発生した例外の報告が必要なケースがある。

ExceptionGroupは例外インスタンスのリストをまとめ、同時に創出できるようにする。
ExceptionGroupも例外のため他の例外と同じように捕捉できる。

def f():
    excs = [OSError('error 1'), SystemError('error 2')]
    raise ExceptionGroup('there were problems', excs)

f()
  + Exception Group Traceback (most recent call last):
  |   File "<stdin>", line 1, in <module>
  |     f()
  |     ~^^
  |   File "<stdin>", line 3, in f
  |     raise ExceptionGroup('there were problems', excs)
  | ExceptionGroup: there were problems (2 sub-exceptions)
  +-+---------------- 1 ----------------
    | OSError: error 1
    +---------------- 2 ----------------
    | SystemError: error 2
    +------------------------------------
try:
    f()
except Exception as e:
    print(f'caught {type(e)}: e')

caught <class 'ExceptionGroup'>: e

ExceptionGroupの例外の処理

https://atmarkit.itmedia.co.jp/ait/articles/2211/11/news021.html

パターン1 ExceptionGroupでまとめられている個々の例外をexpect*節で処理する方法

try:
    # ExceptionGroup例外を送出するコード
except* TypeError as e:
    # ExceptionGroup例外に格納されているTypeError例外を処理
except* ValueError as e:
    # ExceptionGroup例外に格納されているValueError例外を処理

パターン2 ExceptionGroup全体をまとめて処理する方法

try:
    # ExceptionGroup例外を送出するコード
except ExceptionGroup as eg:
    # ExceptionGroup例外に格納されている例外を個別に取り出して処理する

9 クラス

https://docs.python.org/ja/3/tutorial/classes.html

9.2. Python のスコープと名前空間

https://docs.python.org/ja/3/tutorial/classes.html#python-scopes-and-namespaces

名前空間(ネームスペース)とは名前からオブジェクトへの対応づけのことを指す。
属性という言葉は.に続く名前全てに対して使っている。
例えば式z.realrealはオブジェクトzの属性。

名前空間はさまざまな時点で作成され、寿命もさまざま。

9.2.1. スコープと名前空間の例

https://docs.python.org/ja/3/tutorial/classes.html#scopes-and-namespaces-example

def scope_test():
    def do_local():
        spam = "local spam"

    def do_nonlocal():
        nonlocal spam
        spam = "nonlocal spam"

    def do_global():
        global spam
        spam = "global spam"

    spam = "test spam"
    do_local()
    print("After local assignment:", spam)
    do_nonlocal()
    print("After nonlocal assignment:", spam)
    do_global()
    print("After global assignment:", spam)

scope_test()
print("In global scope:", spam)

出力結果

After local assignment: test spam
After nonlocal assignment: nonlocal spam
After global assignment: nonlocal spam
In global scope: global spam

do_local()...スコープ:do_local関数内
do_nonlocal()...スコープ:do_nonlocal...scope_test関数内
do_global()...スコープ:モジュールレベル

9.3.1. クラス定義の構文

https://docs.python.org/ja/3/tutorial/classes.html#class-definition-syntax

クラス定義の単純な形式

class ClassName:
    <statement-1>
    .
    .
    .
    <statement-N>

9.3.2. クラスオブジェクト

https://docs.python.org/ja/3/tutorial/classes.html#class-objects

クラスオブジェクトは2種類の演算、属性参照とインスタンス生成をサポートしている。

属性参照にはobj.nameを使う

class MyClass:
    """A simple example class"""
    i = 12345

    def f(self):
        return 'hello world'

クラスのインスタンス化には関数記法を使う。クラスオブジェクトのことをクラスの新しいインスタンスを返す引数のない関数のように扱う。

x = MyClass()

上記はクラスの新しいインスタンスを作成し、そのオブジェクトをローカル変数xへ格納する。

インスタンス化操作ではからのオブジェクトが作成される。多くのクラスは特定の初期状態でカスタマイズされたオブジェクトを作成したい。
そのためクラスには_init_()という特殊メソッドが定義されている。新しくインスタンス化された際に自動的に_init_()を呼び出す。

def __init__(self):
    self.data = []
class Complex:
    def __init__(self, realpart, imagpart):
        self.r = realpart
        self.i = imagpart

x = Complex(3.0, -4.5)
x.r, x.i
(3.0, -4.5)

9.3.3. インスタンスオブジェクト

https://docs.python.org/ja/3/tutorial/classes.html#instance-objects

インスタンオブジェクトでできる操作は属性の参照。有効な属性名はデータ属性とメソッドの2種類がある。

データ属性

x.counter = 1
while x.counter < 10:
    x.counter = x.counter * 2
print(x.counter)
del x.counter

9.3.4. メソッドオブジェクト

https://docs.python.org/ja/3/tutorial/classes.html#method-objects

メソッドとはオブジェクトの属するメソッドのこと

x.f()
xf = x.f
while True:
    print(xf())

メソッドが呼び出された時、実際には何が起きているか?

f()の関数定義では引数を一つ指定していたにも関わらず上記ではx.f()にて引数なしで呼び出されている。
なぜ呼び出せるかというと引数にインスタンスオブジェクトが渡されるため

x.f()という呼び出しはMyClass.f(x)と等価である。

9.3.5. クラスとインスタンス変数

https://docs.python.org/ja/3/tutorial/classes.html#class-and-instance-variables

以下コードは間違い

tricksがクラス変数として扱われ、別の犬間で同じtricksが共有されてしまうため。

class Dog:

    tricks = []             # クラス変数の間違った使用

    def __init__(self, name):
        self.name = name

    def add_trick(self, trick):
        self.tricks.append(trick)

>>> d = Dog('Fido')
>>> e = Dog('Buddy')
>>> d.add_trick('roll over')
>>> e.add_trick('play dead')
>>> d.tricks                # 意図せず すべての犬で共有
['roll over', 'play dead']

以下正しい例

class Dog:

    def __init__(self, name):
        self.name = name
        self.tricks = []    # 犬ごとに新しい空リストを作る

    def add_trick(self, trick):
        self.tricks.append(trick)

>>> d = Dog('Fido')
>>> e = Dog('Buddy')
>>> d.add_trick('roll over')
>>> e.add_trick('play dead')
>>> d.tricks
['roll over']
>>> e.tricks
['play dead']

9.4. いろいろな注意点

https://docs.python.org/ja/3/tutorial/classes.html#random-remarks

以下ではf,g,hは全てCの属性であり,関数オブジェクトを参照している。
全てCのインスタンスメソッドである。

# クラス外で定義された関数
def f1(self, x, y):
    return min(x, x+y)

class C:
    f = f1

    def g(self):
        return 'hello world'

    h = g

メソッドはself引数のメソッド属性を使って他のメソッドを呼び出すことができる

class Bag:
    def __init__(self):
        self.data = []

    def add(self, x):
        self.data.append(x)

    def addtwice(self, x):
        self.add(x)
        self.add(x)

9.5. 継承

https://docs.python.org/ja/3/tutorial/classes.html#inheritance

Python には継承に関係する 2 つの組み込み関数がある

  • isinstance()を使うとインスタンスの型が調べられる。
    • isinstance(obj, int) は obj.__class__intintの派生クラスの場合に限りTrueになります。
  • issubclass()を使うと継承関係が調べられる。
    • bool は int のサブクラスなので issubclass(bool, int)True です。しかし、 floatint のサブクラスではないので issubclass(float, int)False です。

9.6. プライベート変数

https://docs.python.org/ja/3/tutorial/classes.html#private-variables

オブジェクトの中からしかアクセス出来ない "プライベート" インスタンス変数は、 Python にはありません。しかし、ほとんどの Python コードが従っている慣習があります

しかしアンダースコアで始まる名前は非publicなものとして扱う慣習がある。ex)_spam

名前マングリングは、サブクラスが内部のメソッド呼び出しを壊さずにオーバーライドするのに便利。

class Mapping:
    def __init__(self, iterable):
        self.items_list = []
        self.__update(iterable)

    def update(self, iterable):
        for item in iterable:
            self.items_list.append(item)

    __update = update   # 元の update() メソッドのプライベートコピー

class MappingSubclass(Mapping):

    def update(self, keys, values):
        # update() への新シグネチャ導入
        # しかし __init__() は壊さない
        for item in zip(keys, values):
            self.items_list.append(item)

上記ではもしMappingSubClass__update識別子を導入しても機能する。
Mappingクラスでは_Mapping__updateとなり_MappingSubclass__updateにそれぞれ置き換えるからである。

9.7. 残りのはしばし(@dataclass)

https://docs.python.org/ja/3/tutorial/classes.html#odds-and-ends

名前つきのデータ要素をひとまとめにするデータ方dataclasses

from dataclasses import dataclass

@dataclass
class Employee:
    name: str
    dept: str
    salary: int
john = Employee('john', 'computer lab', 1000)
john.dept
'computer lab'
john.salary
1000

9.8. イテレータ (iterator)

https://docs.python.org/ja/3/tutorial/classes.html#iterators

イテラブルとイテレータに関して
https://yumarublog.com/python/iter/

イテラブルとは

for文で要素を一つずつ取り出せるような反復可能なオブジェクトのこと

  • シーケンス型(リスト、タプル、range)
  • マッピング型(辞書)
  • テキストシーケンス型(文字列)
  • バイナリシーケンス型(bytes、bytearray、memoryview)

イテレータに関して

型の一つで順番に要素を取得できるオブジェクトのこと

9.9 ジェネレータ

https://docs.python.org/ja/3/tutorial/classes.html#generators

ジェネレータはイテレータを作成するためのツール

ジェネレータは通常の関数のように書かれるが何らかのデータを返す際にはyieldを使う

以下例

def reverse(data):
    for index in range(len(data)-1, -1, -1):
        yield data[index]
for char in reverse('golf'):
    print(char)

f
l
o
g

9.10. ジェネレータ式

https://docs.python.org/ja/3/tutorial/classes.html#generator-expressions

リスト内包表記のように書くが[]ではなく()で囲むのが特徴

ジェネレータ式は完全なジェネレータの定義よりコンパクトですが、ちょっと融通の効かないところがあります。 同じ内容を返すリスト内包表記よりはメモリに優しいことが多いという利点があります。

sum(i*i for i in range(10))                 # 平方和
285

xvec = [10, 20, 30]
yvec = [7, 5, 3]
sum(x*y for x,y in zip(xvec, yvec))         # ドット積
260

10. 標準ライブラリミニツアー

https://docs.python.org/ja/3/tutorial/stdlib.html

10.3. コマンドライン引数

https://docs.python.org/ja/3/tutorial/stdlib.html#command-line-arguments

sys

# File demo.py
import sys
print(sys.argv)

上記を以下のように実行

$ python demo.py one two three

出力結果

$ ['demo.py', 'one', 'two', 'three']

argparse

コマンドライン引数を処理するための機能

import argparse

parser = argparse.ArgumentParser(
    prog='top',
    description='Show top lines from each file')
parser.add_argument('filenames', nargs='+')
parser.add_argument('-l', '--lines', type=int, default=10)
args = parser.parse_args()
print(args)

上記を以下のように実行

$ python top.py --lines=5 alpha.txt beta.txt

結果は以下
args.lines5args.filenames['alpha.txt', 'beta.txt']に設定。

10.5. 文字列のパターンマッチング

https://docs.python.org/ja/3/tutorial/stdlib.html#string-pattern-matching

import re
re.findall(r'\bf[a-z]*', 'which foot or hand fell fastest')
# ['foot', 'fell', 'fastest']
re.sub(r'(\b[a-z]+) \1', r'\1', 'cat in the the hat')
# 'cat in the hat'

上記解説

  • re.findall...指定された式表現に一致する部分文字列をリストとして返す。
    • r'\bf[a-z]*'
      • \b単語の境界を表す
      • f...fにマッチするか
      • [a-z]*小文字のアルファベット全体にマッチするか(f)で始まる文字列全体
  • re.sub...指定された正規表現に一致する文字列を指定された文字列に置き換えます
    • r'(\b[a-z]'...一文字以上からなる単語を認識
    • +) \1...最初に正規表現にて認識した単語の同じ文字列をマッチさせる。
    • r'\1'...第二引数の置き換え文字列最初に認識した単語へ置き換え

つまり上記場合、全単語を調べる上でthe theを認識し、1個目に認識したtheへ置き換える。

10.7. インターネットへのアクセス

https://docs.python.org/ja/3/tutorial/stdlib.html#internet-access

インターネットにアクセスしたりインターネットプロトコルを処理したりするための多くのモジュールがあります。最も単純な2つのモジュールは、 URL からデータを取得するための urllib.request と、メールを送るための smtplib です

urllib.request

from urllib.request import urlopen
with urlopen('http://worldtimeapi.org/api/timezone/etc/UTC.txt') as response:
    for line in response:
        line = line.decode()             # Convert bytes to a str
        if line.startswith('datetime'):
            print(line.rstrip())         # Remove trailing newline

smtplib

import smtplib
server = smtplib.SMTP('localhost')
server.sendmail('soothsayer@example.org', 'jcaesar@example.org',
"""To: jcaesar@example.org
From: soothsayer@example.org

Beware the Ides of March.
""")
server.quit()

10.9. データ圧縮

https://docs.python.org/ja/3/tutorial/stdlib.html#data-compression

import zlib
s = b'witch which has which witches wrist watch'
len(s)
41

11.4. マルチスレッディング(threading)

https://docs.python.org/ja/3/tutorial/stdlib2.html#multi-threading

スレッド処理 (threading) とは、順序的な依存関係にない複数のタスクを分割するテクニックです。スレッドは、ユーザの入力を受け付けつつ、背後で別のタスクを動かすようなアプリケーションの応答性を高めます。同じような使用例として、I/O を別のスレッドの計算処理と並列して動作させるというものがあります。

import threading, zipfile

class AsyncZip(threading.Thread):
    def __init__(self, infile, outfile):
        threading.Thread.__init__(self)
        self.infile = infile
        self.outfile = outfile

    def run(self):
        f = zipfile.ZipFile(self.outfile, 'w', zipfile.ZIP_DEFLATED)
        f.write(self.infile)
        f.close()
        print('Finished background zip of:', self.infile)

background = AsyncZip('mydata.txt', 'myarchive.zip')
background.start()
print('The main program continues to run in foreground.')

background.join()    # Wait for the background task to finish
print('Main program waited until background was done.')
  • class AsyncZip(threading.Thread):を継承してAsyncZipクラスを作成
  • __init__...入出力ファイルを設定、threading.Thread.スーパークラスのコンストラクタを作成
  • def run(self):にてzipファイル作成と作成完了のログを出す設定
  • background = AsyncZip('mydata.txt', 'myarchive.zip') background.start()にてインスタンス作成とスレッド作成
    • background.start()にてrun()が走る
  • background.join() print('Main program waited until background was done.') にてメインスレッドの処理待ちその後ログ出力

11.7. リスト操作のためのツール

https://docs.python.org/ja/3/tutorial/stdlib2.html#tools-for-working-with-lists

array

from array import array
a = array('H', [4000, 10, 700, 22222])
sum(a)
26932
a[1:3]
array('H', [10, 700])
  • arrray(typecode, イテレータ)
    • typecode...配列内のデータ型を指定する1文字のコード。
      • H...符号なし2バイト
    • イテレータ...配列の初期値となるリストやタプルなどのシーケンス。

collections.deque

collections モジュールでは、 deque オブジェクトを提供しています。リスト型に似ていますが、データの追加と左端からの取り出しが速く、その一方で中間にある値の参照は遅くなります。こうしたオブジェクトはキューや木構造の幅優先探索の実装に向いています

from collections import deque
d = deque(["task1", "task2", "task3"])
d.append("task4")
print("Handling", d.popleft())
Handling task1

- d.popleft()にて左端の要素を取り出しそれ以外を削除

11.8. 10進浮動小数演算

https://docs.python.org/ja/3/tutorial/stdlib2.html#decimal-floating-point-arithmetic

decimal モジュールでは、 10 進浮動小数の算術演算をサポートする Decimal データ型を提供しています
例えば、70 セントの電話代にかかる 5% の税金を計算しようとすると、10 進の浮動小数点値と 2 進の浮動小数点値では違う結果になってしまいます。計算結果を四捨五入してセント単位にしようとすると、以下のように違いがはっきり現れます

from decimal import *
round(Decimal('0.70') * Decimal('1.05'), 2)
Decimal('0.74')
round(.70 * 1.05, 2)
0.73

上の例で、Decimal を使った計算では、末尾桁のゼロが保存されており、有効数字2桁の被乗数から自動的に有効数字を 4 桁と判断しています。Decimal は手計算と 同じ方法で計算を行い、2 進浮動小数が 10 進小数成分を正確に表現できないことに よって起きる問題を回避しています。

12. 仮想環境とパッケージ

https://docs.python.org/ja/3/tutorial/venv.html

12.1. はじめに

https://docs.python.org/ja/3/tutorial/venv.html#introduction

Pythonアプリケーションは特定のライブラリやそのバージョンを必要とすることが多く、異なるアプリケーション間でライブラリのバージョン要求が衝突することがあります。この問題を解決するために 仮想環境 を使用します。
仮想環境は、特定のPythonバージョンと追加パッケージを含む独立した環境を提供し、各アプリケーションがそれぞれの環境で必要なライブラリを自由に管理できます。これにより、ライブラリのバージョン変更が他のアプリケーションに影響を与えることを防ぎます。

12.2. 仮想環境の作成

https://docs.python.org/ja/3/tutorial/venv.html#creating-virtual-environments

以下のようにして仮想環境を作成可能

$ python -m venv tutorial-env{仮想環境名}

これは tutorial-env ディレクトリがなければ作成して、その中に Python インタプリタ、その他関連するファイルのコピーを含むサブディレクトリを作る。

仮想環境を作成後、有効か

$ source tutorial-env/bin/activate

有効化し、Pythonを実行するとその仮想環境のPythonを実行化するようになる。

$ source ~/envs/tutorial-env/bin/activate
(tutorial-env) $ python
Python 3.5.1 (default, May  6 2016, 10:59:36)
  ...
>>> import sys
>>> sys.path
['', '/usr/local/lib/python35.zip', ...,
'~/envs/tutorial-env/lib/python3.5/site-packages']

仮想環境を無効化は以下

$ deactivate

12.3. pip を使ったパッケージ管理

https://docs.python.org/ja/3/tutorial/venv.html#managing-packages-with-pip

pip と呼ばれるプログラムでパッケージをインストール、アップグレード、削除することができます。デフォルトでは pip は Python Package Index からパッケージをインストールします。
ブラウザを使って Python Package Index を閲覧することができます。

(tutorial-env) $ python -m pip install novas
Collecting novas
  Downloading novas-3.1.1.3.tar.gz (136kB)
Installing collected packages: novas
  Running setup.py install for novas
Successfully installed novas-3.1.1.3

バージョンの指定
パッケージ名のあとに == とバージョン番号を付けることで、特定のバージョンのパッケージをインストールすることもできる。

(tutorial-env) $ python -m pip install requests==2.6.0
Collecting requests==2.6.0
  Using cached requests-2.6.0-py2.py3-none-any.whl
Installing collected packages: requests
Successfully installed requests-2.6.0

python -m pip show...指定されたパッケージの情報を表示

(tutorial-env) $ python -m pip show requests
---
Metadata-Version: 2.0
Name: requests
Version: 2.7.0
Summary: Python HTTP for Humans.
Home-page: http://python-requests.org
Author: Kenneth Reitz
Author-email: me@kennethreitz.com
License: Apache 2.0
Location: /Users/akuchling/envs/tutorial-env/lib/python3.4/site-packages
Requires:

python -m pip list...仮想環境にインストールされた全てのパッケージを表示。

(tutorial-env) $ python -m pip list
novas (3.1.1.3)
numpy (1.9.2)
pip (7.0.3)
requests (2.7.0)
setuptools (16.0)

requirements.txt...バージョン管理システムにコミットして、アプリケーションの一部として配布することができます。ユーザーは必要なパッケージをpip install -rでインストールできる。

(tutorial-env) $ python -m pip install -r requirements.txt
Collecting novas==3.1.1.3 (from -r requirements.txt (line 1))
  ...
Collecting numpy==1.9.2 (from -r requirements.txt (line 2))
  ...
Collecting requests==2.7.0 (from -r requirements.txt (line 3))
  ...
Installing collected packages: novas, numpy, requests
  Running setup.py install for novas
Successfully installed novas-3.1.1.3 numpy-1.9.2 requests-2.7.0

Discussion