Closed20

【読書メモ】Python チュートリアル

tez3998tez3998

引数の受け渡し

引数はsys.argvという名前のリストに格納される。

tez3998tez3998

ソースコードの文字コード

デフォルトでは、ソースコードはUTF-8でエンコードされているものとして扱われる。
標準ライブラリはASCIIで書かれている。

UTF-8以外の文字コードを使いたいとき

以下のようにファイルの先頭に書けばよい。

# -*- coding: cp1252 -*-

cp1252の部分は、codecsがサポートしている文字コードであれば、なんでもOK。

tez3998tez3998

for文で反復中のコレクションオブジェクトを変更する方法

作戦1:反復する前にコレクションオブジェクトをコピーする

# コレクションオブジェクト
users = {'Hans': 'active', 'Éléonore': 'inactive', '景太郎': 'active'}

for user, status in users.copy().items(): # ここでコピーしてしまう
    if status == 'inactive':
        del users[user] # 安全に削除できる

作戦2:新しいコレクションオブジェクトを作る

# コレクションオブジェクト
users = {'Hans': 'active', 'Éléonore': 'inactive', '景太郎': 'active'}

active_users = {} # 新しいコレクションオブジェクト
for user, status in users.items():
    if status == 'active':
        active_users[user] = status # 新しいコレクションオブジェクトに挿入
tez3998tez3998

range()

forでよく使うrange()、あれは実はrange型のオブジェクトを生成する関数。
ずっとリストを返す関数だと思ってた...。

tez3998tez3998

forとelseの組み合わせ

try-catchのように、forとelseの組み合わせ技があるらしい。

for n in range(2, 10):
    for x in range(2, n):
        if n % x == 0:
            print(n, 'equals', x, '*', n//x)
            break
    else:
        print(n, 'is a prime number') # 2つ目のforがbreakで終了したときは実行されない
tez3998tez3998

Cのswitch文っぽいmatch-case文

当てはまった条件のみが実行される。
途中の未定義に見えるxyは、x, y = pointのようなアンパックが行われたものとして使うことができる。

class Point:
    x: int
    y: int

def where_is(point):
    match point:
        case Point(x=0, y=0):
            print("Origin")
        case Point(x=0, y=y):
            print(f"Y={y}")
        case Point(x=x, y=0):
            print(f"X={x}")
        case Point():
            print("Somewhere else")
        case _: # _は全てにマッチする
            print("Not a point")

where_is("hoge") # "Not a point"

p = Point()
where_is(p) # "Somewhere else"

p.y = 1
where_is(p) # Y=1

|(or)を使うことで、複数条件も記載可能。

case 401 | 403 | 404:
    return "Not allowed"

条件のネストもできる。

match points: # pointsはPointのリストを想定
    case []:
        print("No points")
    case [Point(0, 0)]:
        print("The origin")
    case [Point(x, y)]:
        print(f"Single point {x}, {y}")
    case [Point(0, y1), Point(0, y2)]:
        print(f"Two on the Y axis at {y1}, {y2}")
    case _:
        print("Something else")
tez3998tez3998

global

global

変数がグローバル変数であることを明示するための文。

globalを使わないと上手く動かない例

g_var = "global" # グローバル変数

def change_global_var():
    g_var = "local" # ここでグローバル変数を変更しているつもりだが...

change_global_var()
print(g_var) # 結果:global。localじゃない!!

globalを使うことで上記の問題を解決した例

g_var = "global" # グローバル変数

def change_global_var():
    global g_var # ここでg_varがグローバル変数を指すことを明示
    g_var = "local" # グローバル変数を変更

change_global_var()
print(g_var) # 結果:local。意図した通りに動いている。
tez3998tez3998

nonlocal

1個外側のスコープの変数を参照することを明示する。

tez3998tez3998

Pythonでの値渡し

実はここでの値とは、オブジェクトの参照のことであってオブジェクトそのものの値ではない。

イミュータブルな変数の値渡し

イミュータブルな値は演算ごとに結果を格納する場所が異なるので、その値が変わると変数も参照先も変える。そのため、以下のコードのように関数内で引数の値を変更しても、その影響は関数外には及ばない。なお、到達不能になった値はガベージ・コレクションによりメモリから削除される。

def change_arg(x):
    x += 10 # 引数を変更

y = 0
change_arg(y)
print(y) # 結果:0

ミュータブルな変数の値渡し

値がイミュータブルだと、値が変更されるたびに新しいメモリの位置に保存されるようなことはされず、変数の参照先も変わらない。そのため、以下のように関数内での変更の影響が関数外にも及ぶ。

def change_arg(x):
    x.append(1) # 引数を変更

y = []
print(y) # 結果:[]
change_arg(y)
print(y) # 結果:[1]
tez3998tez3998

デフォルト引数は1度だけしか評価されない

特にミュータブルなデフォルト引数には注意が必要。

これにより生じる問題

L=[]が実行されるのが1度だけなので、その後はLは更新されない。

def f(a, L=[]):
    L.append(a)
    return L

print(f(1)) # [1]
print(f(2)) # [1, 2]
print(f(3)) # [1, 2, 3]

対策

def f(a, L=None):
    if L is None:
        L = []
    L.append(a)
    return L
tez3998tez3998

引数の渡し方の指定

  • /の前は位置でしか引数を渡せない
  • *の後はキーワードでしか引数を渡せない
  • /*の間であれば、位置で渡してもキーワードで渡してもいい
def combined_example(pos_only, /, standard, *, kwd_only):
    print(pos_only, standard, kwd_only)

# 呼び出し
# "standard"はstandard="standard"でも可
combined_example("pos", "standard", kwd_only="keyword")
tez3998tez3998

任意個数の引数

*を付けると任意個数の引数を受け取れる

def concat(*args, sep="/"):
    return sep.join(args)

concat("earth", "mars", "venus", sep=".") # 出力:earth.mars.venus
tez3998tez3998

辞書型の引数リストのアンパック

**で辞書型をアンパックできる。

def parrot(voltage, state='a stiff', action='voom'):
    print("-- This parrot wouldn't", action, end=' ')
    print("if you put", voltage, "volts through it.", end=' ')
    print("E's", state, "!")

# 関数の引数と同じ名前をキーに持つ辞書型を用意
d = {"voltage": "four million", "state": "bleedin' demised", "action": "VOOM"}
parrot(**d) # アンパック
tez3998tez3998

集合型

a = {x for x in 'abracadabra' if x not in 'abc'} # 'abc'がset()
print(a) # 出力:{'r', 'd'}
tez3998tez3998

__init__.py

ディレクトリをパッケージとして扱わせるためには、__init__.pyが必要。__init__.pyは空でもいいが、パッケージに初期化コードを実行したりしてもいい。

tez3998tez3998

from package import *の挙動の決定

packageディレクトリ下の__init__.pyに

__all__ = ["script_1", "script_2"]

と書くことで、from package import *としたときは、packageという名前のパッケージからscript_1.pyとscript_2.pyを読み込む、という設定ができる。

tez3998tez3998

親パッケージの親パッケージからへの参照

from .. import formats
from ..filters import equalizer
tez3998tez3998

自作イテレータ

__next__というメソッドを持つオブジェクトを返す__iter__というメソッドをクラスに実装すればよい。

class Reverse:
    """Iterator for looping over a sequence backwards."""
    def __init__(self, data):
        self.data = data
        self.index = len(data)

    def __iter__(self):
        return self

    def __next__(self):
        if self.index == 0:
            raise StopIteration
        self.index = self.index - 1
        return self.data[self.index]
tez3998tez3998

ジェネレータ

データを返すときはyieldを使う。next()が呼ばれるたびに、ジェネレータは中断した処理を再開する。

def reverse(data):
    for index in range(len(data)-1, -1, -1):
        yield data[index]
このスクラップは2022/10/26にクローズされました