基礎から学ぶPython入門 90日コース | 実践Python - Day 17:pathlib

90日で身につけるPython
スポンサーリンク
スポンサーリンク
  1. Day 17:pathlibで「ファイルとフォルダ」を安全に扱う
  2. pathlibの基本:Pathオブジェクトを理解する
    1. Path.cwd() と Path() の違い
    2. / 演算子でパスを結合する
  3. フォルダ操作:作成・存在確認・削除の基本
    1. フォルダの存在確認と作成
    2. 親ディレクトリごと作成する(parents=True)
  4. ファイル作成:touch() と書き込みの入り口
    1. 空のファイルを作る(touch)
    2. open() と組み合わせて書き込み
  5. ファイル一覧:iterdir() と glob() の使い分け
    1. iterdir() で「直下の中身」を列挙する
    2. glob() で「パターンに合うファイル」を検索する
    3. rglob() で「サブフォルダも含めて検索する」
  6. ファイル検索:条件付きで探す
    1. 特定サイズ以上のファイルを探す
    2. 更新日時で絞り込む
  7. パス操作:親・名前・拡張子・絶対パス
    1. 親ディレクトリ・ファイル名・拡張子
    2. 絶対パスに変換する
  8. Day 17ミニテンプレート:pathlibファイルユーティリティ
  9. Day 17のまとめ
  10. Day 17:pathlibで「フォルダ分析ツール」を作る
  11. フォルダ分析ツールの全体像をイメージする
    1. どんなことができるツールか
  12. pathlibの基本をフォルダ分析の文脈で押さえる
    1. 対象フォルダを選ぶ関数
  13. ファイル一覧とフォルダ一覧を表示する
    1. iterdir() で直下の中身を列挙する
  14. 再帰的なファイル検索:rglob() を使いこなす
    1. 特定の拡張子のファイルをフォルダ以下すべてから探す
  15. フォルダ配下のファイル数・フォルダ数を集計する
    1. 再帰的に数を数える
  16. パス操作:親・名前・拡張子・絶対パスを理解する
    1. パスの分解を分析に活かす
  17. ファイル作成:分析結果をレポートとして保存する
    1. reports フォルダにテキストレポートを作る
  18. フォルダ分析ツールの全体テンプレート
  19. Day 17(制作編)のまとめ

Day 17:pathlibで「ファイルとフォルダ」を安全に扱う

Day 17では、Pythonの標準ライブラリ pathlib を使って、 ファイル検索・フォルダ操作・パス操作・ファイル作成・ファイル一覧を、初心者の方にも分かりやすい形で学んでいきます。 「文字列でパスをゴリゴリつなぐ世界」から、「パスをオブジェクトとして安全に扱う世界」へ一歩進むイメージです。

pathlibの基本:Pathオブジェクトを理解する

Path.cwd() と Path() の違い

pathlib の中心にあるのが Path クラスです。 これは「ファイルやフォルダへのパス」を表すオブジェクトで、文字列よりも安全で便利に扱えます。

from pathlib import Path

# 現在の作業ディレクトリ(カレントディレクトリ)を取得します
current = Path.cwd()
print("現在のディレクトリ:", current)

# 任意のパス文字列からPathオブジェクトを作成します
home = Path.home()  # ユーザーのホームディレクトリ
print("ホームディレクトリ:", home)

some_path = Path("documents/report.txt")
print("任意のパス:", some_path)
Python
  • Path.cwd() は「今いるフォルダ」を表す Path を返します。
  • Path.home() は「ユーザーのホームディレクトリ」を返します。
  • Path("...") で任意のパス文字列から Path を作れます。

/ 演算子でパスを結合する

文字列で "/" を手動でつなぐ代わりに、Path では / 演算子を使ってパスを結合できます。

from pathlib import Path

base = Path.cwd()
logs_dir = base / "logs"          # フォルダ
log_file = logs_dir / "app.log"   # ファイル

print("ログフォルダ:", logs_dir)
print("ログファイル:", log_file)
Python
  • OSごとの区切り文字(/\)を意識する必要がなくなります。
  • 「フォルダ/ファイル」という構造がコード上でも自然に見えるようになります。

フォルダ操作:作成・存在確認・削除の基本

フォルダの存在確認と作成

from pathlib import Path

logs_dir = Path("logs")

if logs_dir.exists():
    print("logs フォルダはすでに存在しています。")
else:
    logs_dir.mkdir()  # フォルダを1つ作成します
    print("logs フォルダを作成しました。")
Python
  • Path.exists() で「そのパスが存在するか」を確認できます。
  • Path.mkdir() で「フォルダ」を作成できます。

親ディレクトリごと作成する(parents=True)

from pathlib import Path

nested_dir = Path("data/backup/2026")

# 親フォルダもまとめて作成したい場合
nested_dir.mkdir(parents=True, exist_ok=True)
print("ネストされたフォルダを作成しました:", nested_dir)
Python
  • parents=True で「途中のフォルダもまとめて作成」できます。
  • exist_ok=True で「すでに存在していてもエラーにしない」ようにできます。

ファイル作成:touch() と書き込みの入り口

空のファイルを作る(touch)

from pathlib import Path

log_file = Path("logs/app.log")

# 親フォルダがなければ作成しておきます
log_file.parent.mkdir(parents=True, exist_ok=True)

if not log_file.exists():
    log_file.touch()  # 空のファイルを作成します
    print("ログファイルを作成しました:", log_file)
else:
    print("ログファイルはすでに存在しています:", log_file)
Python
  • Path.touch() は「空のファイルを作る」メソッドです。
  • path.parent で「そのファイルが属するフォルダ」を取得できます。

open() と組み合わせて書き込み

Pathopen() メソッドも持っているので、ファイル操作と相性が良いです。

from pathlib import Path

log_file = Path("logs/app.log")
log_file.parent.mkdir(parents=True, exist_ok=True)

# 追記モードでログを書き込みます
with log_file.open("a", encoding="utf-8") as f:
    f.write("アプリを起動しました。\n")

print("ログを書き込みました。")
Python
  • log_file.open("a", ...)open(str(log_file), "a", ...) と同じ意味ですが、Path と一貫性が保てます。

ファイル一覧:iterdir() と glob() の使い分け

iterdir() で「直下の中身」を列挙する

from pathlib import Path

target = Path.cwd()

print("現在のディレクトリの中身:")
for entry in target.iterdir():
    if entry.is_dir():
        kind = "フォルダ"
    elif entry.is_file():
        kind = "ファイル"
    else:
        kind = "その他"

    print(f"{kind}: {entry.name}")
Python
  • Path.iterdir() は「そのフォルダ直下のエントリ」を返します。
  • is_dir()is_file() で「フォルダかファイルか」を判定できます。

glob() で「パターンに合うファイル」を検索する

from pathlib import Path

target = Path.cwd()

print("このフォルダ内の .py ファイル一覧:")
for py_file in target.glob("*.py"):
    print(" -", py_file.name)
Python
  • glob("パターン") は「ワイルドカード付きの検索」を行います。
  • "*.py" は「拡張子が .py のファイル」を意味します。

rglob() で「サブフォルダも含めて検索する」

from pathlib import Path

target = Path.cwd()

print("このフォルダ以下すべての .txt ファイル一覧:")
for txt_file in target.rglob("*.txt"):
    print(" -", txt_file)
Python
  • rglob() は「再帰的な glob」であり、サブフォルダも含めて検索します。

ファイル検索:条件付きで探す

特定サイズ以上のファイルを探す

from pathlib import Path

target = Path.cwd()
threshold = 1024 * 100  # 100KB以上

print(f"{target} 以下で、サイズが100KB以上のファイルを探します。")

for file in target.rglob("*"):
    if file.is_file():
        size = file.stat().st_size
        if size >= threshold:
            print(f"{file} ({size} bytes)")
Python
  • file.stat().st_size で「ファイルサイズ(バイト)」を取得できます。
  • 条件付きでフィルタすることで、「重いファイルだけ」「古いファイルだけ」などの検索ができます。

更新日時で絞り込む

from pathlib import Path
from datetime import datetime, timedelta

target = Path.cwd()
days = 7  # 7日以内に更新されたファイル

limit = datetime.now() - timedelta(days=days)

print(f"{target} 以下で、直近 {days} 日以内に更新されたファイルを探します。")

for file in target.rglob("*"):
    if file.is_file():
        mtime = datetime.fromtimestamp(file.stat().st_mtime)
        if mtime >= limit:
            print(f"{file} (更新日時: {mtime})")
Python
  • datetime.fromtimestamp() で「UNIXタイム → datetime」に変換できます。
  • timedelta(days=...) と比較することで、「最近更新されたファイル」を探せます。

パス操作:親・名前・拡張子・絶対パス

親ディレクトリ・ファイル名・拡張子

from pathlib import Path

p = Path("logs/app.log")

print("フルパス:", p)
print("親ディレクトリ:", p.parent)
print("ファイル名:", p.name)
print("拡張子:", p.suffix)
print("拡張子なしの名前:", p.stem)
Python
  • parent → 親ディレクトリ
  • name → ファイル名(拡張子込み)
  • suffix → 拡張子(例:.log
  • stem → 拡張子を除いた名前

絶対パスに変換する

from pathlib import Path

p = Path("logs/app.log")
abs_path = p.resolve()

print("相対パス:", p)
print("絶対パス:", abs_path)
Python
  • resolve() は「実際の絶対パス」を返します(シンボリックリンクなども解決します)。

Day 17ミニテンプレート:pathlibファイルユーティリティ

最後に、Day 17で学んだ内容をまとめた「簡易ファイルユーティリティ」のテンプレートを示します。

# day17_pathlib_tool.py
# pathlibを使ったファイル・フォルダ操作の練習ツール

from pathlib import Path
from datetime import datetime, timedelta


def choose_directory():
    text = input("対象フォルダのパスを入力してください(空ならカレントディレクトリ): ").strip()
    if text == "":
        directory = Path.cwd()
    else:
        directory = Path(text)

    if not directory.exists():
        print("指定されたフォルダは存在しません。")
        return None
    if not directory.is_dir():
        print("指定されたパスはフォルダではありません。")
        return None

    return directory


def list_files(directory: Path):
    print(f"\n=== {directory} の直下のファイル一覧 ===")
    for entry in directory.iterdir():
        if entry.is_file():
            print("ファイル:", entry.name)
        elif entry.is_dir():
            print("フォルダ:", entry.name)


def search_py_files_recursive(directory: Path):
    print(f"\n=== {directory} 以下の .py ファイル一覧 ===")
    for py_file in directory.rglob("*.py"):
        print(py_file)


def search_recent_files(directory: Path, days: int = 7):
    limit = datetime.now() - timedelta(days=days)
    print(f"\n=== 直近 {days} 日以内に更新されたファイル ===")
    for file in directory.rglob("*"):
        if file.is_file():
            mtime = datetime.fromtimestamp(file.stat().st_mtime)
            if mtime >= limit:
                print(f"{file} (更新日時: {mtime})")


def create_log_file(directory: Path):
    logs_dir = directory / "logs"
    logs_dir.mkdir(parents=True, exist_ok=True)

    log_file = logs_dir / "app.log"
    if not log_file.exists():
        log_file.touch()
        print("ログファイルを作成しました:", log_file)
    else:
        print("ログファイルはすでに存在しています:", log_file)


def main():
    directory = choose_directory()
    if directory is None:
        return

    while True:
        print("\n=== pathlib ファイルユーティリティ ===")
        print(f"対象フォルダ: {directory}")
        print("1: 直下のファイル・フォルダ一覧を表示する")
        print("2: .py ファイルを再帰的に検索する")
        print("3: 直近7日以内に更新されたファイルを検索する")
        print("4: logs/app.log を作成する")
        print("5: 終了する")

        choice = input("番号を入力してください: ").strip()

        if choice == "1":
            list_files(directory)
        elif choice == "2":
            search_py_files_recursive(directory)
        elif choice == "3":
            search_recent_files(directory, days=7)
        elif choice == "4":
            create_log_file(directory)
        elif choice == "5":
            print("pathlib ファイルユーティリティを終了します。")
            break
        else:
            print("1〜5の番号を入力してください。")


if __name__ == "__main__":
    main()
Python

Day 17のまとめ

Day 17では、pathlib を通して、

  • パスを「文字列」ではなく「オブジェクト」として扱う感覚
  • フォルダ作成・存在確認・親ディレクトリ取得などの基本操作
  • iterdir()glob()rglob() によるファイル一覧・ファイル検索
  • touch()open() によるファイル作成と書き込みの入り口
  • 親・名前・拡張子・絶対パスといったパス操作の基本

をステップバイステップで体験していただきました。

ここまで来ると、「ファイルとフォルダを扱うコードが、ぐっと読みやすく・安全になる」という感覚が少し育っているはずです。 この感覚は、今後の実践Python編でログ管理・バックアップ・自動化スクリプトなどを作っていくうえで、強力な土台になっていきます。


Day 17:pathlibで「フォルダ分析ツール」を作る

Day 17の制作編では、pathlib を中心に、 ファイル検索・フォルダ操作・パス操作・ファイル作成・ファイル一覧を組み合わせた「フォルダ分析ツール」を作っていきます。 フォルダの中身をざっくり把握したり、特定のファイルだけを抽出したりできる、実用的なCLIツールを目指します。

フォルダ分析ツールの全体像をイメージする

どんなことができるツールか

今回のフォルダ分析ツールでは、次のような機能を持たせます。

  • 対象フォルダを指定する
  • 直下のファイル・フォルダ一覧を表示する
  • 再帰的にファイルを検索する(例:*.py*.txt
  • フォルダ配下のファイル数・フォルダ数を集計する
  • 分析結果をテキストファイルとして保存する(ファイル作成)

この流れの中で、

  • ファイル検索rglob()
  • フォルダ操作mkdir()exists()is_dir()
  • パス操作parentnamesuffixresolve()
  • ファイル作成touch()open()
  • ファイル一覧iterdir()

を自然に使っていきます。

pathlibの基本をフォルダ分析の文脈で押さえる

対象フォルダを選ぶ関数

まずは、ユーザーに「分析したいフォルダ」を入力してもらい、Path オブジェクトとして扱えるようにします。

from pathlib import Path

def choose_directory():
    """対象フォルダをユーザーに入力してもらい、Pathとして返す関数です。"""
    text = input("分析したいフォルダのパスを入力してください(空ならカレントディレクトリ): ").strip()

    if text == "":
        directory = Path.cwd()  # 現在のディレクトリ
    else:
        directory = Path(text)  # 入力された文字列からPathを作成

    if not directory.exists():
        print("指定されたフォルダは存在しません。")
        return None

    if not directory.is_dir():
        print("指定されたパスはフォルダではありません。")
        return None

    return directory
Python

ここでの重要ポイントは、

  • Path.cwd() で「今いるフォルダ」を簡単に取得できること
  • Path(text) で「文字列 → パスオブジェクト」に変換できること
  • exists()is_dir() で「存在するか」「フォルダかどうか」をチェックできること

です。これにより、「存在しないパス」や「ファイルをフォルダとして扱ってしまう」ミスを防ぎやすくなります。

ファイル一覧とフォルダ一覧を表示する

iterdir() で直下の中身を列挙する

フォルダ分析の基本は、「中に何があるか」を一覧することです。 iterdir() を使って、直下のファイル・フォルダを表示する関数を作ります。

from pathlib import Path

def show_direct_children(directory: Path):
    """対象フォルダ直下のファイル・フォルダ一覧を表示する関数です。"""
    print(f"\n=== {directory} の直下の一覧 ===")

    has_entries = False
    for entry in directory.iterdir():
        has_entries = True
        if entry.is_dir():
            kind = "フォルダ"
        elif entry.is_file():
            kind = "ファイル"
        else:
            kind = "その他"

        print(f"{kind}: {entry.name}")

    if not has_entries:
        print("このフォルダには何もありません。")
Python

ここでは、

  • iterdir() で「直下のエントリ」を1つずつ取り出しています。
  • is_dir()is_file() で種類を判定し、ユーザーに分かりやすく表示しています。

フォルダ分析ツールの「最初の一歩」として、とても重要な機能です。

再帰的なファイル検索:rglob() を使いこなす

特定の拡張子のファイルをフォルダ以下すべてから探す

フォルダ分析では、「このフォルダ以下の .py ファイルを全部見たい」「.txt だけ抽出したい」といったニーズがよくあります。 rglob() を使うと、サブフォルダも含めて検索できます。

from pathlib import Path

def search_files_by_pattern(directory: Path, pattern: str):
    """指定したパターンに合うファイルを再帰的に検索して表示する関数です。"""
    print(f"\n=== {directory} 以下の {pattern} に合うファイル一覧 ===")

    found = False
    for file in directory.rglob(pattern):
        if file.is_file():
            found = True
            print(file)

    if not found:
        print("該当するファイルは見つかりませんでした。")
Python

使い方の例:

# .pyファイルを探す
search_files_by_pattern(directory, "*.py")

# .txtファイルを探す
search_files_by_pattern(directory, "*.txt")
Python

ここでの重要ポイントは、

  • rglob("*.py") で「サブフォルダも含めて .py ファイルを検索」できること
  • パターンを引数にしておくことで、汎用的な検索関数になること

です。

フォルダ配下のファイル数・フォルダ数を集計する

再帰的に数を数える

フォルダ分析ツールらしく、「このフォルダ以下にファイルが何個、フォルダが何個あるか」を集計する関数を作ります。

from pathlib import Path

def count_entries_recursive(directory: Path):
    """フォルダ以下のファイル数・フォルダ数を再帰的に集計する関数です。"""
    file_count = 0
    dir_count = 0

    for entry in directory.rglob("*"):
        if entry.is_file():
            file_count += 1
        elif entry.is_dir():
            dir_count += 1

    print(f"\n=== {directory} 以下の集計結果 ===")
    print(f"ファイル数: {file_count}")
    print(f"フォルダ数: {dir_count}")
Python

ここでは、

  • rglob("*") で「すべてのエントリ」を対象にしています。
  • is_file()is_dir() で種類を判定し、カウンタを増やしています。

これにより、「このプロジェクトはどれくらいの規模か」をざっくり把握できるようになります。

パス操作:親・名前・拡張子・絶対パスを理解する

パスの分解を分析に活かす

フォルダ分析では、「どのフォルダにどんな拡張子のファイルがあるか」を知りたいこともあります。 parentnamesuffixstem を使うと、パスを柔軟に扱えます。

from pathlib import Path

def show_path_details(path: Path):
    """1つのパスについて、親・名前・拡張子などを表示する関数です。"""
    print("\n=== パスの詳細情報 ===")
    print("フルパス:", path)
    print("絶対パス:", path.resolve())
    print("親ディレクトリ:", path.parent)
    print("ファイル名:", path.name)
    print("拡張子:", path.suffix)
    print("拡張子なしの名前:", path.stem)
Python

この関数は、分析ツールの中で「特定のファイルを詳しく見たい」ときに使えます。

ファイル作成:分析結果をレポートとして保存する

reports フォルダにテキストレポートを作る

フォルダ分析ツールを「実用的なツール」に近づけるために、分析結果をテキストファイルとして保存する機能を追加します。

from pathlib import Path
from datetime import datetime

def save_report(directory: Path, file_count: int, dir_count: int):
    """分析結果を reports フォルダにテキストレポートとして保存する関数です。"""
    reports_dir = directory / "reports"
    reports_dir.mkdir(parents=True, exist_ok=True)  # 親ごと作成

    # 日付入りのファイル名を作成します
    timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
    report_file = reports_dir / f"report_{timestamp}.txt"

    # ファイルを作成して書き込みます
    with report_file.open("w", encoding="utf-8") as f:
        f.write(f"フォルダ分析レポート\n")
        f.write(f"対象フォルダ: {directory}\n")
        f.write(f"作成日時: {datetime.now()}\n")
        f.write("\n")
        f.write(f"ファイル数: {file_count}\n")
        f.write(f"フォルダ数: {dir_count}\n")

    print("分析レポートを保存しました:", report_file)
Python

ここでの重要ポイントは、

  • directory / "reports" で「対象フォルダ配下に reports フォルダ」を作ること
  • mkdir(parents=True, exist_ok=True) で「安全にフォルダを作成」していること
  • open("w", ...) で「テキストファイルを作成し、分析結果を書き込んでいる」こと

です。これにより、「分析結果を後から見返せる」ツールになります。

フォルダ分析ツールの全体テンプレート

ここまでの関数を組み合わせて、「フォルダ分析ツール」として動く全体コードをまとめます。

# day17_folder_analyzer.py
# pathlibを使ったフォルダ分析ツール

from pathlib import Path
from datetime import datetime


def choose_directory():
    text = input("分析したいフォルダのパスを入力してください(空ならカレントディレクトリ): ").strip()
    if text == "":
        directory = Path.cwd()
    else:
        directory = Path(text)

    if not directory.exists():
        print("指定されたフォルダは存在しません。")
        return None
    if not directory.is_dir():
        print("指定されたパスはフォルダではありません。")
        return None

    return directory


def show_direct_children(directory: Path):
    print(f"\n=== {directory} の直下の一覧 ===")
    has_entries = False
    for entry in directory.iterdir():
        has_entries = True
        if entry.is_dir():
            kind = "フォルダ"
        elif entry.is_file():
            kind = "ファイル"
        else:
            kind = "その他"
        print(f"{kind}: {entry.name}")
    if not has_entries:
        print("このフォルダには何もありません。")


def search_files_by_pattern(directory: Path, pattern: str):
    print(f"\n=== {directory} 以下の {pattern} に合うファイル一覧 ===")
    found = False
    for file in directory.rglob(pattern):
        if file.is_file():
            found = True
            print(file)
    if not found:
        print("該当するファイルは見つかりませんでした。")


def count_entries_recursive(directory: Path):
    file_count = 0
    dir_count = 0

    for entry in directory.rglob("*"):
        if entry.is_file():
            file_count += 1
        elif entry.is_dir():
            dir_count += 1

    print(f"\n=== {directory} 以下の集計結果 ===")
    print(f"ファイル数: {file_count}")
    print(f"フォルダ数: {dir_count}")

    return file_count, dir_count


def save_report(directory: Path, file_count: int, dir_count: int):
    reports_dir = directory / "reports"
    reports_dir.mkdir(parents=True, exist_ok=True)

    timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
    report_file = reports_dir / f"report_{timestamp}.txt"

    with report_file.open("w", encoding="utf-8") as f:
        f.write("フォルダ分析レポート\n")
        f.write(f"対象フォルダ: {directory}\n")
        f.write(f"作成日時: {datetime.now()}\n")
        f.write("\n")
        f.write(f"ファイル数: {file_count}\n")
        f.write(f"フォルダ数: {dir_count}\n")

    print("分析レポートを保存しました:", report_file)


def main():
    directory = choose_directory()
    if directory is None:
        return

    while True:
        print("\n=== フォルダ分析ツール(pathlib版) ===")
        print(f"対象フォルダ: {directory}")
        print("1: 直下のファイル・フォルダ一覧を表示する")
        print("2: パターンでファイル検索する(例: *.py)")
        print("3: フォルダ以下のファイル数・フォルダ数を集計する")
        print("4: 集計結果をレポートとして保存する")
        print("5: 終了する")

        choice = input("番号を入力してください: ").strip()

        if choice == "1":
            show_direct_children(directory)
        elif choice == "2":
            pattern = input("検索パターンを入力してください(例: *.py, *.txt): ").strip()
            if pattern:
                search_files_by_pattern(directory, pattern)
            else:
                print("パターンが空です。")
        elif choice == "3":
            file_count, dir_count = count_entries_recursive(directory)
        elif choice == "4":
            file_count, dir_count = count_entries_recursive(directory)
            save_report(directory, file_count, dir_count)
        elif choice == "5":
            print("フォルダ分析ツールを終了します。")
            break
        else:
            print("1〜5の番号を入力してください。")


if __name__ == "__main__":
    main()
Python

Day 17(制作編)のまとめ

Day 17の制作編では、「フォルダ分析ツール」を通して、

  • Path を使った安全で読みやすいパス操作
  • iterdir()rglob() によるファイル一覧・ファイル検索
  • exists()is_dir() によるフォルダの存在確認とエラー防止
  • mkdir()open() によるフォルダ作成・レポートファイル作成
  • フォルダ配下のファイル数・フォルダ数の集計と、その結果の保存

を一つのプログラムの中で体験していただきました。

ここまで来ると、「pathlibを使うと、ファイルとフォルダを扱うコードがぐっと整理される」という感覚が少し育っているはずです。 この感覚は、今後の実践Python編でログ管理・バックアップ・自動分析ツールなどを作っていくうえで、強力な土台になっていきます。

タイトルとURLをコピーしました