おもこん

おもこんは「思いつくままにコンピュターの話し」の省略形です

Python初級者のお勉強ノート(11)クラス、インスタンス、メソッド

クラスの説明からは「初心者」から「初級者」にタイトルを変えることにします。 カッコ内の番号は通し番号にします。

プログラムをクラスという仕組みにまとめることができます。

例えば、電卓プログラムを作るとしましょう。
プログラムの中の次のような部分をまとめてクラスCalcを作ります。
なお、Calcはcalculator(電卓)の最初の4文字を取ったものです。
また、クラス名は大文字で始めることになっています。

  • 電卓(のクラスCalc)を初期化して使える状態にする
  • 電卓で加減算の計算をする
  • 計算結果を電卓の中に保持をする

ここでは、簡単な電卓プログラムを通じてクラスの基本を学びます。


クラス、インスタンスインスタンス変数、メソッドとは?

クラス、インスタンスインスタンス変数、メソッドはセットで理解するようにしましょう。

  • クラスは設計図のようなもの。例えば電卓のクラスは、それが保持するデータや外部に提供する加減算のプログラムを定義している
  • インスタンスはクラスをもとに作られる。これは、設計図をもとに実物を作るようなもの。例えば、電卓のクラスから、2つの「calcA」「calcB」などのインスタンスを作ることができる。それぞれのインスタンスは独立して動作する
  • インスタンス変数はその名の通り、インスタンスに関連付けられた変数で、「インスタンス名.変数名」(インスタンス名+ドット+変数名)という形で参照される
  • メソッドは、クラスに定義された関数で、個々の「インスタンス名.メソッド名(引数の並び)」(インスタンス名+ドット+メソッド名+カッコ内に引数の並び)の形で呼び出すことができる。これをメソッドという。例えば、電卓のクラスにおいて、足し算をaddという名前の関数で定義していたとする。Calcクラスのインスタンスxを通してx.add(3, 4)の形で呼び出すことができる

クラス定義の基本

クラス定義にはclassキーワードを使います。その基本構文は以下のとおりです。

class クラス名:
    def __init__(self, 引数1, 引数2, ...):
        # 初期化処理
        ... ... ...
    
    def メソッド名(self, 引数1, 引数2, ...):
        # メソッドの処理
        ... ... ...

この構文で、クラスの名前を定義し、その中に関数や変数をまとめることで、一つのまとまった設計図を作ることができます。

__init__とは?

クラスの中で特別な名前の関数として__init__があります。 これは「初期化関数」と呼ばれ、インスタンスが生成されるときに自動的に呼び出されます(インスタンスの生成方法は後ほど説明します)。 この関数を利用して、インスタンスの初期状態を設定できます。

関数とメソッドの違い

クラス定義内で定義された関数は、インスタンスを通じて呼び出されます。 このときは「関数」といわず「メソッド」と呼びます。 つまり、「メソッド」はインスタンスを通して呼ばれるクラス定義内の関数のことです。

クラス定義内の関数の第1引数は、この関数がメソッドとして呼ばれたときのインスタンスです。 慣例的にこれをselfと書くことにしています。 他の名前にすることは可能ですが、それはしないでください。 常にselfと書くことにより、プログラムを理解しやすくなります。

class Calc:
    ... ... ...
    ... ... ...
    def add(self, a, b):
        self.m = a + b
        return self.m

このプログラムでは、クラスCalcの定義の中で関数addを定義しています。 addの第1引数のselfは、インスタンスを指し、それ以外に2つの引数abがあります。

外部からこのメソッドを呼ぶためには、まずインスタンスの生成が必要です。 仮に生成されたインスタンスxとします。 すると、このメソッドはx.add(10, 20)のような形で呼び出されます。 呼び出し時には、関数定義のabの部分だけが引数に書かれ、selfの部分は書きません。 selfはシステムが自動的に第一引数にして、関数addを呼び出します。

インスタンス変数

インスタンス変数は、「インスタンス名.変数名」(インスタンス名+ドット+変数名)の形で参照される変数です。 仮に、Calcクラスのインスタンスを生成し、それを変数xに代入したとします。 x.mは、変数xの指すインスタンスインスタンス変数です。

クラス内の関数定義では第1引数のselfを使ってインスタンス変数を参照します。 さきほどの変数xはクラスの外部で生成されていることに注意してください。 したがって、xをクラスの内部で使うことはできません。 なお、self予約語(プログラム言語で使われるワードで、例えばdef, return, classなど)ではありません。 単に関数の第1引数を表す慣例的な変数です。 したがって、クラス定義の中の関数定義の外では使うことはできません。

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

クラス内では、インスタンスごとに異なる値を保持する「インスタンス変数」と、クラス全体で共有される「クラス変数」を定義できます。

以下の例では、クラス変数total_calculatorsを追加して、作成された電卓インスタンスの数を記録します。

class Calc:
    total_calculators = 0  # クラス変数

    def __init__(self, m=0):
        self.m = m  # インスタンス変数
        Calc.total_calculators += 1  # クラス変数を更新

このように、クラス変数はすべてのインスタンスで共有され、全体に関連するデータを扱うのに便利です。

クラス変数を外部から参照するときは「クラス名.変数名」(クラス名+ドット+変数名)を使います。

print(Calc.total_calculators)  # クラス変数 total_calculators を表示する

クラス変数やインスタンス変数は、そのクラスやインスタンスの外部でも定義することができます。 なお、「定義」されるのは、最初に変数に代入をしたときです。 しかし、外部での定義は混乱をもたらす原因となるので、極力避けるべきです。 常に定義は内部で行い、外部からは定義済みの変数を読み書きするようにしましょう。

インスタンスの生成

クラス定義の後に、インスタンスを生成できます。 インスタンスの生成は、クラス名で関数呼び出しのようにすればできます。

x = Calc()

これで変数xにCalcクラスのインスタンスが代入されます。

クラスの例:Calcクラス(電卓のクラス)

以下は電卓クラスCalcの定義例です。このクラスには次の機能があります。

  • 記憶した値(メモリ)を持つ
  • 加算、減算を行い、結果を記憶する
  • 記憶した値をリセットする
  • 初期値をインスタンス生成時に渡せる
class Calc:
    total_calculators = 0

    def __init__(self, m=0):
        self.m = m
        Calc.total_calculators += 1

    def add(self, a, b=None):
        if b is None:
            self.m += a
        else:
            self.m = a + b
        return self.m

    def sub(self, a, b=None):
        if b is None:
            self.m -= a
        else:
            self.m = a - b
        return self.m

    def reset(self):
        self.m = 0
  • total_calculatorsはクラス変数。インスタンスを生成するごとに1だけ増え、インスタンスの個数を保持する
  • __init__関数はインスタンス生成時に自動的に呼び出される。インスタンスの初期化のための関数。2番目の引数mはクラス生成時の引数(例えばx = Class(5)の引数5)が入る。インスタンス変数self.mが電卓内のメモリを表し、そのメモリを引数で初期化する。引数がなければデフォルト値の0にする。インスタンス数を記録するクラス変数Calc.total_calculatorsを1だけ増やす
  • addsubはそれぞれ足し算と引き算のメソッド。2つの引数が与えられたときはその和または差を計算し、メモリに入れ、計算結果を返す。引数が1つの時は、メモリと引数の和または差を計算し、メモリに入れ、結果を返す
  • resetはメモリをゼロにする

電卓を使ってみる

以下は電卓クラスのインスタンスを操作する例です。

# 初期値を指定してインスタンスを生成
calc = Calc(10)

# 二項の加算
print(calc.add(3, 4))  # 結果: 7

# 一項の加算(記憶している値に加える)
print(calc.add(5))  # 結果: 12

# 二項の減算
print(calc.sub(10, 3))  # 結果: 7

# 一項の減算(記憶している値から引く)
print(calc.sub(2))  # 結果: 5

# メモリのリセット
calc.reset()
print(calc.m)  # 結果: 0

# 作成されたインスタンスの数
print(Calc.total_calculators)  # 結果: 1

クラスを使うメリット

クラスを使うと、次のような場面でプログラムが整理しやすくなります。

  • データと操作の統一管理: 計算結果(データ)と加算・減算(操作)を1つのクラスにまとめられる。
  • 再利用性の向上: 必要に応じて複数のインスタンスを作り、それぞれ独立して動作させられる。

Python初心者のお勉強ノート(10)関数の基礎

プログラムを効率よく書くためには、同じ処理を繰り返し使える「関数」を理解することが重要です。

関数とは何か

関数とは、ひとまとまりの処理を名前を付けて定義したものです。 一度定義すれば、何度でもその処理を呼び出して使うことができます。

関数の定義と呼び出し

Pythonで関数を定義するには、defキーワードを使います。以下は基本的な構文です。

def 関数名(引数1, 引数2, ...):
    処理
    return 戻り値

例として、2つの数を足し合わせる関数を作ってみましょう。 この関数を呼び出すには、関数名と引数を指定します。

>>> def add(a, b):
...     return a+b
...
>>> add(2, 4)
6
>>>

引数のない関数

引数を取らない関数も定義できます。例えば、文字列"Hello, world."を返す関数を作ります。

>>> def greet():
...     return "Hello world."
...
>>> greet()
'Hello world.'
>>>

引数が無くても、関数名に続くカッコは省略できないことに注意しましょう。

戻り値のない関数

returnを省略すると、戻り値はNoneというオブジェクトになります。 Noneは何もないという意味で使われます。

次の例では、引数を2倍にして表示するだけの関数を作ります。

>>> def double_print(x):
...     print(x*2)
...
>>> a = double_print(10)
20
>>> a
>>> print(a)
None
>>>
  • 5行目では、関数内のprint文が実行され、10の2倍の20が表示される
  • 6行目で変数aの値を表示させようとしているが、Pythonを対話的に動かしているときにNoneは表示されないことになっている
  • 7行目ではprint文でaの値を表示する。その結果が8行目のNoneになっている

良く使うprint関数も戻り値がNoneになっています。 なお、関数の中でreturn NoneとしてもNoneを返すことができます。

デフォルト引数

関数にデフォルト値を設定することで、引数を省略した場合に使われる値を指定できます。

>>> def greet(name="world"):
...     print(f"Hello, {name}!")
...
>>> greet()
Hello, world!
>>> greet("Alice")
Hello, Alice!
>>>

可変長引数

引数の数を固定しない関数を、*argsまたは**kwargsを使い、作ることができます。 関数呼び出しでは、任意の引数を与えることができ、関数内ではそれをタプル、または辞書型として処理できます。

  • *args: 任意の数の位置引数をタプルとして受け取る
  • **kwargs: 任意の数のキーワード引数を辞書型として受け取る

次の例では任意の個数の数値を引数にできます。 関数側では、引数をタプルとして受け取っています。 sumはタプルやリストの合計を求める関数です。

>>> def summarize(*args):
...     return sum(args)
...
>>> summarize(1, 2, 3, 4, 5)
15
>>>

次の例では可変長のキーワード引数を使って関数定義をしています。 キーワード引数とは、辞書型のように、キーと値のペアで各引数を与えるものです。

>>> def display_info(**kwargs):
...     for key, value in kwargs.items():
...     print(f"{key.capitalize()}: {value}")
...
>>> display_info(name="Alice", age=30, country="USA")
Name: Alice
Age: 30
Country: USA
>>> display_info()
>>> display_info(language="Python", version="3.11", author="Guido van Rossum")
Language: Python
Version: 3.11
Author: Guido van Rossum

関数を使うメリット

関数は、特定の処理を再利用したいときやコードを簡潔にしたいときに役立ち、複雑なアルゴリズムを分割して分かりやすくできるという利点があります。 また、プログラムが読みやすくなるとともに、バグの場所がどこにあるかが分かりやすくなり、修正が容易になるというメリットもあります。

Python初心者のお勉強ノート(9)辞書型

辞書型は、Pythonの中でも非常に重要で便利なデータ型の1つです。この記事では、辞書型の基本的な使い方から、よく使うプログラムテクニック、さらには応用例として単語カウントプログラムの解説までを取り上げます。

辞書型とは?

辞書型(dict)は、キーと値のペアを扱うデータ型です。
たとえば、英語の辞書を考えると、「単語」がキーで、その意味が「値」に相当します。
Pythonの辞書型は、このようにデータを整理し、効率的にアクセスするために使います。

辞書型の作り方

辞書型は、波括弧 {} を使って定義します。

d = {"apple": "りんご", "banana": "バナナ", "cherry": "さくらんぼ"}

ここで: - "apple""banana""cherry" がキー - "りんご""バナナ""さくらんぼ" が値です。

キーを指定して値にアクセスします。

print(d["apple"])  # 出力: りんご

値を更新したり、新しいキーと値を追加することもできます。

d["apple"] = "アップル"  # 更新
d["orange"] = "オレンジ"  # 新規追加
print(d)  # 出力: {'apple': 'アップル', 'banana': 'バナナ', 'cherry': 'さくらんぼ', 'orange': 'オレンジ'}

辞書型でよく使うテクニック

1. キーの存在確認

辞書型には、指定したキーが存在するかどうかを確認する方法があります。

if "apple" in d:
    print("りんごは存在します")
else:
    print("りんごは存在しません")

2. 辞書のループ

辞書のキーや値を順番に処理するにはforループを使います。

for key, value in d.items():
    print(f"{key} は {value} です")

3. 辞書の初期化とgetメソッド

辞書のキーに対応する値が存在しない場合に、エラーを避けてデフォルト値を返すには、getメソッドを使います。

print(d.get("mango", "キーが存在しません"))  # 出力: キーが存在しません

この例では、辞書dにmangoというキーが無いので、getメソッドの2番目の引数「キーが存在しません」が返されました。 もし、dがmangoというキーを持っていれば、そのキーに対応する値が返されます。

4. 要素の削除

辞書からキーと値のペアを削除するには、delを使います。

del d["banana"]
print(d)  # 出力: {'apple': 'アップル', 'cherry': 'さくらんぼ', 'orange': 'オレンジ'}

応用例:単語カウントプログラム

辞書型は、データを効率よく集計するのに便利です。ここでは、文章中の単語の出現回数をカウントするプログラムを例に解説します。

プログラム

def count_words(text):
    words = text.split()
    word_counts = {}
    for word in words:
        word = word.lower().strip(".,!?;:")
        word_counts[word] = word_counts.get(word, 0) + 1
    return word_counts

text = "Hello, world! Hello Python. Hello Python programming."
result = count_words(text)
print(result)  # 出力: {'hello': 3, 'world': 1, 'python': 2, 'programming': 1}

解説

  1. 単語分割:

    • split()で文字列を空白で区切り、単語のリストを作ります。
  2. 正規化:

    • 各単語を小文字に変換し、末尾の句読点をstrip()で取り除きます。
  3. 辞書でカウント:

    • getメソッドを使い、辞書に単語が存在しない場合は0を初期値として加算します。
  4. 結果:

    • 辞書型を使って単語ごとの出現回数を保存し、最終的にその結果を返します。

辞書型の使いどころ

  1. データの集計:

    • 単語カウントや、カテゴリごとのデータ整理などに便利です。
  2. 設定値の管理:

    • アプリケーションの設定をキーと値で管理する際に使われます。
  3. データの高速アクセス:

    • キーを指定して値を素早く取得できるため、検索操作が多い場面で役立ちます。例えば、プログラミング言語のシンボル・テーブルはその代表例です。

辞書型は、データを整理し効率的に扱うための強力なツールです。 使われる頻度はそれほど多くはありませんが、上記の使いどころなどでは、威力を発揮します。

Python初心者のお勉強ノート(8)正規表現入門

文字列の中から特定のパターンを探したり、置き換えたりする場合に正規表現(regular expression)が役立ちます。Pythonではreモジュールを使って正規表現を扱います。

reモジュールとは?

Pythonのreモジュールは正規表現を使った文字列処理をサポートする標準モジュールです。このモジュールをインポートすることで、正規表現に関連するさまざまな関数を利用できます。

import re

正規表現リテラル

正規表現では、特別な記号やパターンを使って条件を表現します。これを「正規表現リテラル」と呼びます。Pythonでは文字列の先頭にrを付けた「raw文字列」を使うと便利です。例えばr"\d+"と書くと、そのまま正規表現として扱えます。後で詳しく説明しますが、\dは任意の数字、+はその前の文字の1回以上の繰り返しを意味します。この正規表現パターンには"1234"や"567890"がマッチします。

pattern = r"\d+"  # 数字の1回以上の繰り返し

正規表現の基本操作

パターンにマッチするか確認する

match関数を使うと、文字列の先頭が特定のパターンに一致するかを確認できます。

s = "Hello world."
pattern = r"Hello"

if re.match(pattern, s):
    print("先頭に'Hello'があります")
else:
    print("先頭に'Hello'はありません")

文字列中の全ての一致箇所を探す

findall関数は、文字列中のすべてのマッチ箇所をリストとして返します。

s = "apple, orange, banana, apple"
pattern = r"apple"

matches = re.findall(pattern, s)
print(matches)  # ['apple', 'apple']

パターンに一致する部分を置き換える

sub関数を使うと、指定したパターンに一致する部分を別の文字列に置き換えられます。

s = "I like apples and bananas."
pattern = r"apples"
new_string = re.sub(pattern, "oranges", s)
print(new_string)  # I like oranges and bananas.

よく使う正規表現の構文と例

文字クラス [ ]

角括弧[ ]内に指定した文字のいずれか1つにマッチします。

pattern = r"[aeiou]"  # 母音にマッチ
s = "banana"
print(re.findall(pattern, s))  # ['a', 'a', 'a']

任意の1文字 .

ピリオド.は任意の1文字にマッチします。

pattern = r"a.c"
s = "abc, aac, acc"
print(re.findall(pattern, s))  # ['abc', 'aac', 'acc']

繰り返し *, +, ?

  • *: 直前の文字の0回以上の繰り返し
  • +: 直前の文字の1回以上の繰り返し
  • ?: 直前の文字の0回または1回
pattern = r"ba*"
s = "b, ba, baa"
print(re.findall(pattern, s))  # ['b', 'ba', 'baa']

pattern = r"ba+"
print(re.findall(pattern, s))  # ['ba', 'baa']

pattern = r"ba?"
print(re.findall(pattern, s))  # ['b', 'ba', 'ba']

特定の位置にマッチする ^$

  • ^: 文字列の先頭にマッチ
  • $: 文字列の末尾にマッチ
pattern = r"^Hello"
s = "Hello world."
print(bool(re.match(pattern, s)))  # True

pattern = r"world\.$"
print(bool(re.search(pattern, s)))  # True

なお、最後の正規表現リテラルで、ピリオドの前にバックスラッシュがありました。 このバックスラッシュは直後の文字の特別な意味を無くし、その文字そのものにします。 なぜなら、バックスラッシュがないと、ピリオドは「0個以上の前の文字の繰り返し」になってしまいます。 ここでは、ピリオドそのものを意味するために、バックスラッシュを使っています。

数字や英字を表す\d\w

  • \d: 数字にマッチ
  • \w: 英数字とアンダースコアにマッチ
pattern = r"\d+"
s = "123 apples"
print(re.findall(pattern, s))  # ['123']

pattern = r"\w+"
s = "Hello_world"
print(re.findall(pattern, s))  # ['Hello_world']

正規表現用語の解説

  • 文字クラス: [ ]の中に指定した文字をグループ化し、そのいずれかにマッチします。
  • 量指定子: *, +, ?など、繰り返しの回数を指定する記号です。
  • エスケープシーケンス: \d\wのように、特定の文字やパターンを短縮して表現するものです。

注意点と参考となるウェブサイト

正規表現は強力ですが、複雑になるとコードが読みにくくなります。まずは基本をしっかり理解し、簡単な場面で使うことから始めましょう。

参考となるウェブサイトを示しておきます。

Python初心者のお勉強ノート(7)文字列の演算とメソッド

文字列の操作は頻繁に行われます。 数字の操作よりもはるかに頻度が高いでしょう。

文字列操作には、演算子を使うものと、文字列のメソッドを使うものがあります。 それに加え、正規表現も多く使われますが、それは次回に回したいと思います。

文字列の演算子による操作

ここでは、+、*、比較演算子、in演算子をとりあげます。

文字列の連結

+演算子を使うと、複数の文字列を1つに連結できます。 Pythonを立ち上げ、対話的に使い、+演算子がどのように動作するか見てみましょう。

>>> h = "Hello"
>>> w = "world"
>>> s = h + " " + w + "."
>>> s
'Hello world.'
>>>

文字列の繰り返し

*演算子を使うと、文字列を指定した回数だけ繰り返せます。

>>> h = "Hello"
>>> s = h * 3
>>> s
'HelloHelloHello'
>>>

文字列の比較

== 演算子で、2つの文字列が同じかどうかを判定できます。

>>> s = "apple"
>>> t = "apple"
>>> s == t
True
>>>

大文字と小文字は異なる文字として扱われます。

>>> "Apple" == "apple"
False
>>>

!=は「等しくない」ことを判定します。 文字列が違えばTrue、同じならばFalseを返します。

<, >, <=, >=は、文字列の辞書順(アルファベット順)で比較する場合に使います。 この比較では1文字ずつ、その文字コードの大小で比べていきますので、大小比較は文字コードに依存します。 アルファベットの範囲で、ASCIIやUTF-8を使っているならば、

  • 大文字は小文字よりも小さい
  • 大文字同士、小文字同士ならば、アルファベットの前の文字が小さく、後ろの文字が大きいとなります。
>>> "apple" < "banana"
True
>>>

文字列の比較では==!=が、他と比べ圧倒的に良く使われます。 不等式は文字列を辞書順に並べたい、ソートしたいとき以外はあまり使わないでしょう。

文字列が含まれているか確認するin 演算子

in 演算子で、ある文字列が別の文字列に含まれているかを調べられます。 含まれている場合はTrue、含まれていない場合はFalseを返します。

>>> s = "Hello world."
>>> "world" in s
True
>>> "Python" in s
False
>>>

文字列のメソッド

メソッドとは、特定のオブジェクトの種類(後の記事で扱うが、これをクラスという)に定義されている操作です。 例えば、"abc""def"などは文字列で、文字列に定義されている操作がメソッドです。 メソッドは「オブジェクト.メソッド名(引数の列)」という形で呼び出されます。 例えば"abc".upper()は文字列オブジェクトの"abc"においてメソッドupperを呼び出します。 このドットの前に書かれたオブジェクトをメソッドのレシーバといいます。 文字列オブジェクトはすべて同じメソッドを持っています。

関数とメソッドは似てますが、関数は特定のオブジェクトとの関連はありません。

文字列のメソッドには便利なものがたくさんあります。ここでは、よく使われるメソッドをいくつか紹介します。

split

レシーバの文字列を、引数で指定した区切り文字で分割し、リストを返します。 デフォルトの区切り文字は空白です。 「デフォルト」という言葉はこれまでもたびたび出てきましたが、特に指定がないときのものをいいます。 ですから、splitメソッドで引数がなければ、デフォルトの空白が区切り文字として使われます。

>>> s = "apple orange banana"
>>> s.split()
['apple', 'orange', 'banana']
>>>

特定の区切り文字を指定した場合。

>>> s = "apple,orange,banana"
>>> s.split(",")
['apple', 'orange', 'banana']
>>>

join

引数にリストをとり、その各要素を、レシーバ(区切り文字)で連結して、1つの文字列にします。

>>> t = ["apple", "orange", "banana"]
>>> " ".join(t)
'apple orange banana'
>>>

replace

文字列中の特定の文字や文字列を別のものに置き換えた新しい文字列を返します。

>>> s = "I like apples."
>>> s.replace("apples", "bananas")
'I like bananas.'
>>> s
'I like apples.'
>>>

Pythonの文字列は不変なので、replaceメソッドがレシーバの文字列を変更することはありません。 常に新しく文字列を作り、それを返します。 元の文字列sが変わっていないことからも、それが分かります。

strip

文字列の先頭と末尾から指定した文字を削除した新しい文字列を作り、それを返します。 デフォルトでは空白を削除します。

>>> s = "  Hello world.  "
>>> s.strip()
'Hello world.'
>>>

特定の文字を指定することもできます。

>>> s = "---Hello world.---"
>>> s.strip("-")
'Hello world.'
>>>

しかし、空白を取り除くことに使うことが多いでしょう。 前後の空白を取り除くことをトリミングともいいます。

find

指定した文字列が最初に見つかる位置を返します。見つからない場合は -1 を返します。

>>> s = "apple orange banana"
>>> s.find("orange")
6
>>>

文字列中の位置は、最初の文字を0番目としてカウントします。 これは、リストの最初の要素が0番目であることと同じです。 人間は日常的に1から数え始めますが、コンピュータでは0から数え始める方が普通です。

in演算子とfindメソッドは文字列中の部分文字列に関する操作である点は似ていますが、使い方が違います。

  • in演算子: 文字列中に部分文字列があるかないかの判断で使う
  • findメソッド: 文字列中の部分文字列の位置を知りたいときに使う

この使い方を踏まえないと、理解しにくいプログラムになるので注意が必要です。

lowerとupper

文字列の中のアルファベットをすべて小文字または大文字に変換します。 アルファベット以外は変換されません。

>>> s = "Hello world."
>>> s.upper()
'HELLO WORLD.'
>>> s.lower()
'hello world.'
>>> s
'Hello world.'
>>>

繰り返しになりますが、文字列は不変なので、レシーバが変更されることはありません。 それは、6-7行目で、変数sによって表されている文字列オブジェクトが不変であることから確認できます。

startswithとendswith

文字列が特定の文字列で始まるか、終わるかをチェックして、True または False を返します。

>>> s = "Hello world."
>>> s.startswith("Hello")
True
>>> s.endswith("world")
False
>>> s.endswith(".")
True
>>>

メソッド名がstartswithで、startwithではない(startの次にsがある)ことに注意してください。 これを間違えるとエラーになります。

Python初心者のお勉強ノート(6)標準入力と標準出力、ファイルの読み書き

標準入出力とは?

標準入力と標準出力は、Unix系のオペレーティングシステム(OS)の仕組みから来ています。 プログラムが起動されると、OSが以下の入出力先を自動的に用意します。

  • 標準入力(stdin): デフォルトでキーボードが割り当てられる。
  • 標準出力(stdout): デフォルトで画面が割り当てられる。

標準出力を使う

次の例では、標準出力に文字列を出力しています。

print("Hello world.")

このプログラムをhelloworld.pyという名前で保存し、コマンドラインからpy helloworld.pyと実行すると、print()関数の出力が画面に表示されます。

標準出力のリダイレクト

標準出力の出力先はコマンドラインで変更できます。 以下の例では、出力先をファイルに切り替えています。

> py helloworld.py > output.txt

このコマンドを実行すると、output.txtHello world.が保存されます。標準出力を使えば、コマンド起動時にその出力先を変えることができ、柔軟性が増します。

標準入力とinput()関数

input()関数とは?

input()関数は、標準入力(デフォルトではキーボード)からデータを受け取ります。次の例では、プロンプトを表示してユーザー入力を受け取ります。

x = input("x = ")

この例では、

  • 引数の文字列"x = "を画面に表示する。これが、入力を促すプロンプトになる
  • キーボードから入力した文字列がxに代入される。

実行例:

>>> x = input("x = ")
x = 100
>>> x
'100'
>>>

数値入力を受け取る

入力されたデータは文字列として扱われます。数値として計算する場合は、型変換を行う必要があります。

x = float(input("x = "))
y = float(input("y = "))
print(f"x + y = {x + y}")

print関数の中の文字列はf-stringと呼ばれる文字列リテラルです。 「フォーマット済み文字列リテラル」ということもありますが、長いのでf-stringが良いでしょう。 このリテラルを詳しく説明すると長くなるので、ここでは次の2点だけ確認しておきます。

  • ダブル・クォートの前にfをつける
  • ダブル・クォート内の文字列に、波カッコで囲んだ式を入れることができる。式は評価され、その値を文字列にしたものが埋め込まれる

sys.stdinを使った高度な標準入力

Pythonsys.stdinは、標準入力を直接扱うためのファイルオブジェクトです。これにより、キーボード入力だけでなく、リダイレクトされた入力データも扱えます。

標準入力をそのまま標準出力に流す

次のコードは、標準入力を1行ずつ読み取り、標準出力にそのまま出力します。

import sys

for line in sys.stdin:
    print(line, end="")

sys.stdinは、標準入力のストリームを直接扱うことができるオブジェクトです。 これをforループと組み合わせると、変数lineに1行ずつ入れてfor文のスイートを繰り返し実行することができます。

print関数の引数で「end=""」の部分は文字列の最後に空文字列を入れる、つまり何も入れないということを意味します。 「end=""」を省略すると、改行"\n"が入ります。 文字列lineには標準入力から入ってくる改行も含まれているので、print関数で新たに改行を入れるべきではありません。

このコードをstdin2stdout.pyというファイル名で保存し、入出力をリダイレクトして実行します。

> cat stdin2stdout.py | py stdin2stdout.py >stdin2stdout-copy.py
> cat stdin2stdout-copy.py
import sys

for line in sys.stdin:
    print(line, end="")
>

1行目でコピーをしています。 2行目ではコピー先のファイルをcatで表示しています。 これでコピーが正しくできていたことがわかります。

ファイル操作の基本

標準入出力は最初からオープンされているので、簡単に使えますが、自分でファイルを新たに使う場合は、オープンとクローズが必要です。

ファイルオブジェクトとは?

ファイルを操作するには、Pythonopen()関数を使い、ファイルオブジェクトを生成します。ファイルオブジェクトは、次のようなメソッドを提供します。

  • 読み取り: read(), readline(), readlines()
  • 書き込み: write(), writelines()
  • 位置変更: seek(), tell()

ファイルのオープンと読み書き

open()関数を使ってファイルを開きます。with構文を使えば、ファイルの自動クローズが保証されるため、安全です。

with open("example.txt", "r", encoding="utf-8") as f:
    content = f.read()
    f.write(content)
  • open関数でファイルを読み込みモード("r"がそれを表している)かつ、UTF-8のコーディングでオープンし、その情報をファイルオブジェクトにして変数fに代入する。
  • f.read関数でファイルを全部読み込み、それを文字列にし、content変数に代入する。
  • 文字列contentを標準出力にwriteメソッドで書きだす。
  • with文のスイートが終了し、外に出ると、ファイルは自動的にクローズされる。

ファイルオブジェクトの詳細

主なメソッド

ファイルオブジェクトは以下の操作をサポートします。

  • read(size): 指定サイズ分を読み取る。サイズを省略するとすべて読み取る。
  • readline(): 1行を読み取る。
  • readlines(): 全行をリストとして取得する。
  • write(string): 文字列をファイルに書き込む。
  • writelines(list): 複数の行をリストから書き込む。

ファイルコピーの例

次のコードは、指定されたファイルを別のファイルにコピーします。 このファイル名がcopy.pyだとします。

import sys

if len(sys.argv) != 3:
    print("Usage: python copy.py <source> <destination>")
    sys.exit(1)

src = sys.argv[1]
dst = sys.argv[2]

try:
    with open(src, "r", encoding="utf-8") as src_file:
        content = src_file.read()
    with open(dst, "w", encoding="utf-8") as dst_file:
        dst_file.write(content)
except FileNotFoundError:
    print(f"Error: The file '{src}' was not found.")
except IOError as e:
    print(f"IOError: {e}")
  • sys.exit(1)関数はプログラムを終了してコマンドに戻る。このとき、1という状態をシステム側に通知している。この数字はOS依存だが、通常0は「正常終了」を表し、それ以外は「異常終了(正常にプログラムが完了しなかった)」を表す。
  • ファイルの読み書きは実行時にエラーが起こる可能性があるので、例外処理を使っている。例外処理の内容は、読み込み時にファイルが見つからなかった場合(FileNotFoundError)とそれ以外の入出力エラー(IOError)に分けている

標準入出力とファイル操作のまとめ

  • 標準入出力:
    • print()input()で基本的な標準入出力が可能。
    • 高度な操作にはsys.stdinsys.stdoutを活用。
  • ファイル操作:
    • open()でファイルオブジェクトを生成し、読み書きを行う。
    • with構文を使うと、リソース管理が安全で簡単。
  • エラー処理:
    • ファイル操作では例外処理を適切に行うことで、エラーに対応する。

標準入出力やファイル操作は、Pythonプログラミングの基本ですから、しっかり押さえておくようにしましょう。

Python初心者のお勉強ノート(5)コマンドライン引数の参照と例外処理

コマンドラインには、引数を書くことができます。 例えば、echo.pyというPythonプログラムは引数をそのまま出力します。

> py echo.py abc
abc

このとき、echo.pyの中では、どのようにして引数を得ているのでしょうか?

sysモジュール

引数を知るためには、sysモジュールを使います。 モジュールとは、Pythonの再利用可能なプログラムです。 sysは、はじめから組み込まれているモジュールです。

モジュールを使うときには、importコマンドを使います。

import sys

このコマンドは通常プログラムの最初に記述します。

sysは、引数をargvというリストにします。 このリストにアクセスするときは、sys.argvのように「モジュール名.リスト名」の形を用います。

import sys

print(sys.argv[1])

これが、echoのプログラムです。 sys.arg[1]は1番目の引数を表します。

  • コマンドラインでは、空白で引数を区切る
  • 引数に空白を入れたいときはシングルクォートまたはダブルクォートで囲む

次の例を見てください。

> py echo.py abc def
abc
> py echo.py 'abc def'
abc def
> py echo.py "abc def"
abc def

echo.py は1番目の引数を表示するので、最初の例ではabcのみが出力され、2番目の引数defは現れません。 残りの例ではクォートで囲んだ部分がまとまって1番目の引数になるのでabc defが表示されます。

なおsys.argv[0]は、プログラム名(例えばecho.py)を表します。

型変換

2つの引数が数字で与えられており、プログラムaddはその和を表示します。

> py add.py 10 15
25.0

引数の和を求めるには

sys.arg[1]+sys.arg[2]

で良さそうですが、うまく動きません。 なぜなら、引数は文字列であり、int型やfloat型ではないからです。 文字列から、intまたはfloatへの変換が必要で、そのための関数はint()やfloat()です。 ここでは、float型に直して計算することにしましょう。

import sys

print(float(sys.argv[1])+float(sys.argv[2]))

print文の引数(かっこの中の部分)は複雑ですが、sys.arg[]で取り出した文字列をfloat()関数に渡してfloat型にしています。

エラーと例外処理

ここは、少し難しい話になりますが、プログラミングでは有益な内容です。 先ほどの関数int()やfloat()は引数が変換可能な数字でないとエラーになります。

  • int("2.5") => 文字列2.5は整数にならないのでエラー
  • float("Hello") => 文字列Helloは数字でないのでエラー

add.pyにおいてユーザが数字変換できない文字列を入力した場合、正しい使い方を表示するのが一般的に用いられる方法です。

また、引数が2つ(以上)なければエラーが起きます。 その場合も正しい使い方の表示をするのが良いでしょう。 例えば次のような表示です。

Usage: py add.py <number> <number>

プログラム実行時にエラーが起きた時に、例外処理として対処するのが有効な方法です。 エラー(error)と例外(exception)は異なる概念なのですが、ここでは詳しい説明は省きます。 先ほどのプログラムで、引数に10とHelloを与えたとします。 プログラムは1行目から順次実行し、次の箇所でエラーが起きます。

print(float(sys.argv[1])+float(sys.argv[2]))

2番目の引数Helloを文字列として取り出すところまではOKで、それをfloatに変換するところでエラーになります。 このエラーは例外を発生させプログラムはストップします。

このように、実行時のエラーは例外を発生させるので、その例外をキャッチしてコントロールできれば、様々なエラー対応が可能です。 これを例外処理といいます。

例外処理は2つの部分の組み合わせになります。 まず、例外が発生する可能性のある部分をtry文で表し、例外が発生した場合に実行するプログラムをexcept文で表します。 具体的には、add.pyを次のように修正します。

import sys

try:
  print(float(sys.argv[1])+float(sys.argv[2]))

except:
  print("Usage: py add.py <number> <number>")

try文のスイート(Pythonの文法では、一連の文のかたまりをスイートといいます。C言語の複文Compound statementに相当します)で例外が発生するとexcept節が実行され、その後try文の次に実行が移ります。 上のプログラムではtry文の次はない(excep節は除いて考えます)ので、プログラムは終了します。

例外処理では、もっと複雑なことができるのですが、ここでは最も簡単な例のみに止めておきます。