📝

Python3エンジニア認定実践試験対策①

2024/12/10に公開

はじめに

職場の皆さんとPython3エンジニア認定実践試験を受けることに。学習資料の作成担当なので先輩に倣い自分もZennで書いてみることにしてみました。
初めての執筆なので、至らない点があるかもしれませんが、どうぞご容赦ください。

教材/実行環境

教材

実行環境

  • Python3.12.7

4: Pythonのクラス

タイトル 問題数 問題割合 備考
4 Pythonのクラス 3 7.5%

クラスの定義と呼び出し

クラスとはオブジェクトを作るためのテンプレートのこと。
オブジェクト??よく分からないですよね。
めちゃめちゃ大ざっばに言うと設計図のようなものです。
イメージするなら

オブジェクト: ロボット
クラス: 腕、足、頭などのパーツの名前、大きさなどの特徴、その機能を定義した設計書

クラスの書き方

  • クラス名の頭文字は大文字
  • 最初は__init__を作る
    • コンストラクタまたはイニシャライザと呼ばれ、クラスを初期化する
  • __init__内で定義する変数にはself.をつける
  • クラス内のメソッドの第一引数はself
from typing import List, Dict
class Team:
    team_type = None
    def __init__(self, tl: Dict[str, str], member_list: List[Dict[str, str]]) -> None:
        self.tl = tl
        self.member_list = member_list

    def add_member(self, new_member: List[Dict[str, str]]) -> None:
        self.member_list.append(new_member)

    def get_member_info(self, name: str) -> Dict[str, str]:
        member_info = [member for member in self.member_list if member["name"] == name]
        return member_info

    def get_all_member_info(self) -> List[Dict[str, str]]:
        all_member = [self.tl] + self.member_list
        return all_member

クラスの使い方

クラスを使えるようにするためにはインスタンス化する必要がある。

インスタンス化: クラス名に()を付けて呼び出すこと。新しいインスタンスが生成される。
# インスタンスを作成
moriyasu_team = Team(
    tl={"name": "吉田麻也", "age": "36歳", "position": "DF"},
    member_list=[
        {"name": "伊藤純也", "age": "31歳", "position": "FW"},
        {"name": "三苫薫", "age": "27歳", "position": "MF"},
        {"name": "川島永嗣", "age": "41歳", "position": "GK"},
    ]
)
type(moriyasu_team)
# <class 'Team'>
moriyasu_team.member_list # moriyasu_teamのmember_listを呼び出す
# [
    {"name": "伊藤純也", "age": "31歳", "position": "FW"},
    {"name": "三苫薫", "age": "27歳", "position": "MF"},
    {"name": "川島永嗣", "age": "41歳", "position": "GK"}
    ]

# moriyasu_teamのadd_memberメソッドを実行
moriyasu_team.add_member(
    {"name": "久保建英", "age": "23歳", "position": "MF"}
)
moriyasu_team.member_list
# [
    {"name": "伊藤純也", "age": "31歳", "position": "FW"},
    {"name": "三苫薫", "age": "27歳", "position": "MF"},
    {"name": "川島永嗣", "age": "41歳", "position": "GK"},
    {"name": "久保建英", "age": "23歳", "position": "MF"}
    ]

# 新たにインスタンスを作成
arashi_team = Team(
    tl={"name": "大野智", "height": "166cm", "member_color": "青"},
    member_list=[
        {"name": "相葉雅紀", "height": "176cm", "member_color": "緑"},
        {"name": "櫻井翔", "height": "171cm", "member_color": "赤"},
        {"name": "二宮和也", "height": "168cm", "member_color": "黄"},
        {"name": "松本潤", "height": "173cm", "member_color": "紫"},
    ]
)
arashi_team.tl() # arashi_teamのtlを呼び出す
# {"name": "大野智", "height": "166cm", "member_color": "青"}

arashi_team.get_member_info("二宮和也") # arashi_teamのget_member_infoメソッドを実行
# {"name": "二宮和也", "height": "168cm", "member_color": "黄"}

特殊メソッド

init()のようにメソッド名の前後にアンダースコア(_)を二個つけたものです。
関数が実行されたときの挙動や演算子の挙動を決めることができます。
特殊メソッドを紹介します。

メソッド 用途
init コンストラクター(初期化時に使われる)
repr 文字列表現
str 文字列型への変換
len 要素数の取得(要素数やサイズを出力するlen()関数で呼ばれる)
eq 等価比較 ==比較演算子
team = Team({"name": "Taro"}, [{"name": "Akira"}, {"name": "Miki"}])
print(team)
# <__main__.Team object at 0x1daa23a7ce0>
from typing import List, Dict
class A_Team:
    def __init__(self, tl: Dict[str, str], member_list: List[Dict[str, str]]) -> None:
        self.tl = tl
        self.member_list = member_list

    # __repr__メソッドでオブジェクトをプリントしたときの表示を変更
    def __repr__(self):
        return f"<A_Team id:{id(self)} tl: {self.tl}>"

    # len()関数の挙動をmember_listの要素数を返すように変更
    def __len__(self):
        return len(self.member_list)

    # 演算子==の挙動をtlが同じ時にTrueを返すように変更
    def __eq__(self, other):
        return self.tl == other.tl

a_team = A_Team({"name": "Taro"}, [{"name": "Akira"}, {"name": "Miki"}])
print(a_team)
# <A_Team id:2038535748880 tl: {'name': 'Taro'}>
print(len(a_team))
# 2

# ==の挙動を確認するため新たなインスタンスを生成
a_team_2 =  A_Team({"name": "Taro"}, [{"name": "Takumi"}, {"name": "Yuuki"}])
a_team_3 =  A_Team({"name": "Gorou"}, [{"name": "keito"}, {"name": "Miki"}])
print(a_team == a_team_2)
# True
print(a_team == a_team_3)
# False

プロパティ化

インスタンスメソッドをプロパティ化すると、括弧を付けずにデータ属性のようにアクセスできます。
プロパティ化することによりgetterメソッドやsetterメソッド、deleterメソッドを明示的に設定できます。

  • インスタンスメソッド: インスタンス化されたクラスが実行するメソッド
  • データ属性: クラス変数とインスタンス変数のこと
    • クラス変数: メソッド外で定義する変数
    • インスタンス変数: メソッド内で定義するself.から始まる変数

プロパティ化する方法

プロパティ化したいメソッドに@propertyをつけるだけ

# Teamクラスのメソッドをプロパティ化してみる
from typing import List, Dict
class B_Team:
    def __init__(self, tl: Dict[str, str], member_list: List[Dict[str, str]]) -> None:
        self.tl = tl
        self.member_list = member_list

    @property
    def get_all_member_info(self) -> List[Dict[str, str]]:
        all_member = [self.tl] + self.member_list
        return all_member

b_team = B_Team({"name": "Taro"}, [{"name": "Akira"}, {"name": "Miki"}])
b_team.get_all_member_info  # データ属性のように()なしでアクセス可能に

クラス継承

継承元や継承先は色々呼び方がありますが、今回は継承元を親クラス、継承先を子クラスと呼ぶことにします。
継承とは元のデータ型である親クラスを使って部分的に機能を変えたり、拡張したりするための機能です。子クラスは親クラスのインスタンス変数やメソッドにアクセスし値を取得したり、メソッドを実行できたりします。
とは言ってもよく分からないと思うので、、、
「犬クラス」、「猫クラス」の二つクラスがあったときに、共通して「鳴く」という機能があるとします。
そんなときに「動物クラス」という親クラスを作ってそれぞれのクラスに継承させることが出来ますよ~というイメージです。

継承する方法

子クラス名の後に(親クラス名)とすることで継承できます。
子クラスから親クラスのメソッドを実行する場合はsuper()関数を使用します。
super().メソッド名で実行することができます。

# 継承構文
class Animal:
    def __init__(self, name, chirping):
        self.name = name
        self.chirping = chirping
    
    def make_sound(self):
        print(f"{self.name}{self.chirping}と鳴きます。")

class Dog(Animal):
    def __init__(self, name, chirping, breed):
        super().__init__(name, chirping)
        self.breed = breed

    def fetch(self):
        print(f"{self.name}はボールを取ってきます。")

class Cat(Animal):
    def __init__(self, name, chirping, color):
        super().__init__(name, chirping)
        self.color = color

    def climb_tree(self):
        print(f"{self.name}は木に登ります。")

dog = Dog(name="ポチ", chirping="ワンワン", breed="柴犬")
cat = Cat(name="ミケ", chirping="ニャーニャー", color="三毛")

# 親クラスのmake_sound()が使える
dog.make_sound()
# ポチはワンワンと鳴きます。

# 親クラスのmake_sound()が使える
cat.make_sound()
# ミケはニャーニャーと鳴きます。

dataclass

データを管理するのに特化したクラス
コンストラクタの__init__()や特殊メソッドの__repr__()などが自動的に定義されます。

データクラス化する方法

@dataclassでdataclassとして宣言することができます。
クラス属性を宣言する際は型ヒントを使用します。

from dataclasses import dataclass
from typing import List, Dict

@dataclass
class C_Team:
    tl: Dict[str, str]
    member_list: List[Dict[str, str]]

c_team = C_Team({"name": "Taro"}, [{"name": "Akira"}, {"name": "Miki"}])
print(c_team.tl)
# {'name': 'Taro'}
print(c_team.member_list)
# [{'name': 'Akira'}, {'name': 'Miki'}]

# 以下のようにclassを定義した場合と同等
class Team:
    def __init__(self, tl: Dict[str, str], member_list: List[Dict[str, str]]) -> None:
        self.tl = tl
        self.member_list = member_list

dataclassデコレータの引数

dataclassデコレーターに引数を与えることもできます。
データ変更ができないようにするfrozen引数を設定します。

@dataclass(frozen=True)
class FrozenTeam:
    tl: Dict[str, str]
    member_list: List[Dict[str, str]]

c_team = C_Team({"name": "Taro"}, [{"name": "Akira"}, {"name": "Miki"}])
c_team.tl = {"name": "Haruka"}
print(c_team)
# C_Team(tl={'name': 'Haruka'}, member_list=[{'name': 'Akira'}, {'name': 'Miki'}])
frozen_team = FrozenTeam({"name": "Taro"}, [{"name": "Akira"}, {"name": "Miki"}])
frozen_team.tl = {"name": "Haruka"}
# FrozenInstanceError: cannot assign to field 'tl'

データ変換

dataclassで宣言したクラスは辞書やタプルに変換する仕組みが提供されています。

関数名 解説 戻り値
asdict(instance) 辞書に変換する dict
astuple(instance) タプルに変換する tuple

オブジェクト関連関数

Pythonのオブジェクトを確認するための組み込み関数を紹介します。

主なオブジェクト関連関数

関数名 解説 戻り値
id(object) 識別値を整数で返す int
type(object) 型オブジェクトを返す type
isinstance(object, classinfo) objectがclassinfoのインスタンスであるか判断する bool

6: テキストの処理

タイトル 問題数 問題割合 備考
6 テキストの処理 4 10.0%
この章は覚えることがメインになるので、基本的にテキストを読んで覚えるのがメインになるのかなと思います。
ただ、何から覚えればいいの?となるかもなので、頻出項目をまとめます。

文字列のチェックメソッド

文字列が指定した形式かどうかをチェックするためのメソッドを紹介します。
返り値は全てbool型です。

メソッド名 解説
isalnum() 文字列が数字と文字のみの場合にTrueを返す。
isalpha() 文字列が文字の場合にTrueを返す。日本語などの非ASCII文字列でも数字や記号を含まなければTrueを返す。
print("abc123".isalnum())
# True
print("abc 123".isalnum())
# False (空白が含まれている)
print("abc!".isalnum())
# False (記号が含まれている)

print("abc".isalpha())
# True
print("abc123".isalpha())
# False (数字が含まれている)
print("あいう".isalpha())
# True (日本語の文字もOK)
print("あい1う".isalpha())
# False (数字が含まれている)

文字列の変換メソッド

文字列を変換するためのメソッドを紹介します。
返り値は全て文字列です。

メソッド名 解説
upper() 文字列を全て大文字に変換する。
lower() 文字列を全て小文字に変換する。
replace(old, new[count]) oldをnewに変換した文字列を返す。countが指定された場合は、先頭から指定した数だけ変換する。
strip([chars]) 文字列の先頭および末尾から指定した文字を全て除去する。charsが指定されていない場合に空白文字が削除される。引数は除去される文字の集合を意味する。
lstrip([chars]) 文字列の先頭から指定した文字を全て除去する。charsが指定されていない場合に空白文字が削除される。引数は除去される文字の集合を意味する。
rstrip([chars]) 文字列の末尾から指定した文字を全て除去する。charsが指定されていない場合に空白文字が削除される。引数は除去される文字の集合を意味する。
print("hello".upper())
# "HELLO"

print("WORLD".lower())
# "world"

print("hello world".replace("world", "Python"))
# "hello Python"
print("abababab".replace("a", "A", 2))
# "AbAbabab" (先頭2つの'a'を'の'A'に)

print("  hello  ".strip())
# "hello"
print("--hello--".strip("-"))
# "hello"

print("    hello  ".lstrip())
# "hello  "
print("--hello--".lstrip("-"))
# "hello--"

print("  hello    ".rstrip())
# "  hello"
print("--hello--".rstrip("-"))
# "--hello"

その他の文字列メソッド

その他よく使用する文字列メソッドを紹介します。

メソッド名 解説 戻り値
split(sep=None, Maxsplit=-1) 文字列を分割する。デフォルトでは空白文字(半角スペース、全角スペース、改行、タブなど)で分割する list
join(iterable) 引数として指定した複数の文字列を結合する str
print("apple,banana,cherry".split(","))
# ['apple', 'banana', 'cherry']
print("one two    three".split())
# ['one', 'two', 'three'] (デフォルトでは空白で分割)
print("one two three".split(maxsplit=1))
# ['one', 'two three'] (最初の空白で分割)

fruits = ['apple', 'banana', 'cherry']
print(", ".join(fruits))
# "apple, banana, cherry"

words = ['Hello', 'World']
print(" ".join(words))
# "Hello World"

フォーマットと文字列リテラル

f-string

f-stringを使うことで文字列中にpythonの式を埋め込むことが出来ます。

name = "Taro"
f"私の名前は{name}です。"
# 私の名前はTaroです。

str.format()

str.format()メソッドはPython3.6より前から使われてきた方法です。

a = 2
b = 3
"aの値は{}です。".format(a) # {}=a
# aの値は2です。
"{1} * {0}".format(a, b) #{0}=a, {1}=b
# 3 * 2
"{x} * {y} = {z}".format(x=a, y=b, z=a*b) #{x}=a, {y}=b, {z}=a*b
# 2 * 3 = 6 

%演算子

文字列 % 値のように書くことで文字列中に値を埋め込みます。

"Hello, %s!" % "Taro" # 文字列埋め込みは%sを使う
# Hello, Taro!
"%(name)s likes %(language)s" % {"name": "Taro", "language": "Python"} #複数指定する場合は辞書またはタプルで指定する

reモジュール

reモジュールは正規表現処理を行うためのモジュールです。
基本的な関数として以下の二つがあります。

  • search(pattern, string, flags=0): 指定された文字列が正規表現にマッチするか調べる
    • 引数:
      • pattern: 正規表現の文字列を指定する
      • string: 正規表現にマッチするか確認する文字列を指定する
      • flags: 正規表現コンパイル時の振る舞いを変更するフラグを指定する
    • 戻り値:
      • マッチした場合はマッチオブジェクト、マッチしなかった場合はNone
  • match(pattern, string, flags=0): 指定された文字列が正規表現にマッチするか調べる。searchとは異なり、文字列の先頭にのみマッチする。
    • 引数、戻り値についてはsearchと同様
import re
re.search("a.c", "abcde")
# <re.Match object; span=(0, 3), match='abc'>
re.match("a.c", "abcde")
# <re.Match object; span=(0, 3), match='abc'>
re.search(".c", "abcde")
# <re.Match object; span=(1, 3), match='bc'>
re.search(".c", "abcde")
# None(何も返ってこない)

reモジュールの定数

reモジュールには正規表現をコンパイルするときに指定するフラグが定数として用意されています。
定数(フラグ)を紹介します。

定数名 解説
I, IGNORECASE 大文字小文字を区別せずにマッチする
import re

text = "Hello, World!"

pattern = r"hello"
match = re.search(pattern, text, re.I)  # re.Iで大文字小文字区別しない
# <re.Match object; span=(0, 5), match='Hello'>

最後に

今回紹介した内容やメソッドの種類は試験範囲のごく一部です。
学習した内容を参考に公式テキスト等を用いて勉強することを強くお勧めします。

Discussion