【Python/Flask】仮想環境構築方法・debug・相対絶対import・f文字フォーマット

この記事は約16分で読めます。

Python仮想環境

通常Pythonのバージョンは、システムにインストールしたひとつのPythonバージョンを利用する。

ただし、中にはPythonライブラリの更新が止まっていて、少し前のPythonバージョンしか対応していないライブラリも存在する。その場合、そのPythonライブラリに合わせ、システムのPythonバージョンをダウングレードさせるのかはとても手間。

言うなれば、スマホのOSを「iOS XXXが利用可能になりました。アップデートしてください」というよく定期的に出てくるバージョンアップの文言にならって、定期的にiOS/Andoroidなどをアップデートしていると思うが、その中の1つのアプリがiOSに対応できなくなったからといって、毎回スマホ全体のiOSをダウングレードわざわざするようなものである。

というわけで、システム全体ではなく、プロジェクト毎に必要なPythonライブラリを管理できた方が良い→ならプロジェクトごとに必要な環境を用意してあげよう=仮想環境作成 という発想になる。

ちなみに、AnacondaやpyenvなどでPythonを動かしている場合は、「仮想環境」毎にPythonのバージョンを変えられるので、上記のようなケースも対応できる。また、Python以外のPHPやNode.jsは、ライブラリをインストールするとプロジェクトフォルダ内にライブラリのフォルダが自動で作成されるので、上記心配は無用。

  • venv(virrtual environmentsの略)を使うと pip によるパッケージの導入状態をプロジェクトごとに独立させることができる。
  • 仮想環境作成→activate→必要なpythonバージョンやパッケージインストール→作業終わったらdeactivateという流れ

venvに関するpython公式サイトこちら

pip と venv を使って仮想環境にパッケージをインストールする - Python Packaging User Guide

仮想環境作成

プロジェクトディレクトリ直下に仮想環境作成する。

Python
$ cd [project dir]
$ python3 -m venv [newenvname]<仮想環境のパス>

python3 -m venv .venv とすると、ディレクトリ直下に.venvというファイル名が作成される。

-mフラグは「module-name」の略で、スクリプトとしてモジュールを実行することを意味する。このフラグを使用して、コマンドラインから直接モジュールを実行できる。

したがって、python3 -m venv .venv というコマンドは、「Python 3のvenvモジュールを使用して、現在のディレクトリに.venvという名前の仮想環境を作成する」という意味になる。

通常、Pythonの仮想環境を作成した後、pip(Pythonのパッケージ管理システム)などのツールはその時点でのデフォルトのバージョンがインストールされる。これらのツールのバージョンが古い場合は、仮想環境内で手動でアップグレードする必要がある。

Python
pip install --upgrade pip

しかし、これは面倒。Python3.9からは、--upgrade-depsというオプションが追加された。このオプションをつけて仮想環境を作成すると、pipなどが最新バージョンの状態で仮想環境が作成される。depsはdependenciesの略。頻繁に新しい環境を作成する場合や、常に最新のツールを使いたい場合に便利。

Python
$ python3 -m venv --upgrade-deps [newenvname]

特定のpythonバージョンで仮想環境を作成したい場合

上記python3コマンドで選択されるPythonバージョンは、その時点で python3 コマンドに関連付けられているPythonのバージョンになる。システムのパス(PATH 環境変数)に設定されているPythonのバージョンだったり、システムにインストールされているPythonの中の最新バージョンだったりする。

Zsh
python3 --version

で表示されるpython version。

python3 -m venv .venv コマンドで作成された仮想環境内では、その環境が作成された時に使用されたPythonバージョンを後から変更することはできない。

特定のpythonバージョン(たとえばpython3.9)で仮想環境を作成したい場合は、そのpythonバージョン名で仮想環境を作成する。

Zsh
python3.9 -m venv .venv

上記コマンドを実行するためには、システム(ホストOS)にpython3.9が元々インストールされている必要がある。python3 -m venv .venv を実行して作成された仮想環境下で特定のpython versionをインストールすることはできない。

一方で、pythonライブラリは、Pythonの標準ライブラリ以外に必要なパッケージを pip を使用してインストールできる。これらのパッケージはその仮想環境に限定され、他の環境やシステム全体には影響しない。

仮想環境の有効化(Activate)

仮想環境を作成しただけでは、まだ仮想環境を利用している状態にはなっていない。仮想環境を有効化する必要がある。

Python
$ source [newenvname]/bin/activate

仮想環境が有効化されると、プロンプトの頭に利用中の仮想環境が()つきで表示される。

pythonののバージョン確認は、--versionオプションまたは-Vオプションを付けて実行。-vではない。

Python
([newenvname])$ python -V
Python 3.12.1

仮想環境を有効化しても、それはそのコマンドプロントやターミナルに限られる。システム全体のPython環境が仮想環境になるわけではない。

そのため、新たにコマンドプロントやターミナルを立ち上げた場合、その中では仮想環境は有効になっていない。複数のコマンドプロントやターミナルで仮想環境を使いたい場合は、それぞれの中で有効化する必要あり。

必要なパッケージインストール

Python
pip install --no-cache-dir -r requirements.txt

--no-cache-dir オプション

  • pipがパッケージのダウンロード時にキャッシュを使わなくなる。これにより、常に最新のパッケージがダウンロードされ、以前のバージョンがキャッシュから誤ってインストールされるのを防げる。
  • ただし、このオプションを使用すると、インストールのたびにパッケージを完全にダウンロードする必要があるため、インターネット接続が遅い環境では時間がかかる可能性がある。
  • requirements.txtにリストされたパッケージをキャッシュなしでインストールする。dirはdirectoryの略。

-r フラグ: “requirements file”(要件ファイル)を意味し、pip に対して与えられたファイルからパッケージ名のリストを読み込み、それらをインストールするよう指示

requirements.txt

  • ルートディレクトリにある前提。ルートディレクトリにない場合には、requirements.txtまでのパスを記載する
  • 必要なパッケージを記載する。下記の場合、pip はFlaskをバージョン1.1.2で、Requestsをバージョン2.24.0以上でインストールし、NumPyはバージョン指定なしで最新バージョンをインストール(バージョンを明記しない場合する。
Zsh
flask==1.1.2
requests>=2.24.0
nump

仮想環境の無効化(Deactivate)

非常にシンプル。仮想環境を有効化した時に利用できるコマンド。

これで、プロンプトに付いていた([newenvname)が無くなり、仮想環境が無効化される。

Zsh
([newenvname])$ deactivate
$

Dockerを使用する際には、source env/bin/activateを実行して仮想環境をアクティブにする必要は基本的にはない。Dockerは、アプリケーションとその依存関係をコンテナ化するためのツールであり、その環境はホストマシンの環境(仮想環境を含む)から独立しているため。

Dockerコンテナ内で動作するアプリケーションの依存関係は、主にDockerfileによって定義される。Dockerfileは、コンテナ内で必要なソフトウェアのインストール、環境設定、実行コマンドなどを記述するもので、このファイルに基づいてDockerイメージが作成される。

Flaskでソースの変更を検知して、Webアプリを自動リロードする方法

  • Flaskをデバッグモードで起動する。main関数にapp.run(debug=True)とするだけ。これにより、コードに変更があった際にサーバーが自動で再起動し、エラー発生時に詳細なトレースバック(遡及:時系列をさかのぼって記録をたどる)を提供する。
Python
if __name__ == "__main__":
    app.run(debug=True)

相対importと絶対import(python3系)

絶対import

  • 「ドット(.)」をつけない import 方法

相対import

  • 「ドット(.)」を使った import 方法
  • 同じパッケージ内で他のモジュールを import する方法。
  • ファイルを相対位置で import する方法ではない。どのファイルからでも相対位置を指し示せば import できるわけではない。

相対importの注意点

  • 実行ファイルから相対 import できない
  • 実行ファイル以外(モジュール同士)は相対 import 可能だが,実行ファイル以上の階層の通るパッケージやモジュールは相対 import できない

Pythonでの「パッケージ」とは、特定の構造を持ったディレクトリ(フォルダ)のことを指す。パッケージ内には、複数のモジュール(Pythonファイル)が含まれることがあり、これらのモジュールは相対的な方法で互いにインポートすることができる。

パッケージと認識されるためには、そのディレクトリに__init__.pyファイルが存在する必要がある。このファイルは空でも構わないが、その存在によってPythonはそのディレクトリをパッケージとして扱う。

具体例

以下のようなディレクトリ構造がある場合

Python
project/

├── main.py

└── mypackage/
    ├── __init__.py
    ├── module1.py
    └── subpackage/
        ├── __init__.py
        └── module2.py
  • projectはプロジェクトのルートディレクトリ。
  • mypackageはPythonのパッケージ。これは__init__.pyファイルが含まれているため。
  • module1.pysubpackageは、mypackageの中にある。
  • subpackageもまたパッケージ(自身の__init__.pyファイルを持っている)。
  • module2.pysubpackage内のモジュール。

相対インポートの例

  1. module1.pyからmodule2.pyをインポートする:
  2. module2.pyからmodule1.pyをインポートする:

実行ファイルと相対インポート

main.pyのようなスクリプト(実行ファイル)からは、相対インポートを使うことはできない。main.pymypackage内のモジュールをインポートする場合は、絶対インポートを使用する。

Python
from mypackage import module1
from mypackage.subpackage import module2
# from .mypackage import module1 は不可。不正な相対インポート

pep8によるimport規約

  • 標準ライブラリ→3rd partyライブラリ→ローカルアプリケーション/ライブラリ固有のインポートの順に1行ずつあけて書く。偉いライブラリ順
  • 複雑なパッケージ構造がない場合には、絶対インポートが推奨されています。これは、絶対インポートの方が読みやすく、通常はより明確なため。
  • 相対インポートは、主に複雑なパッケージ内部でのみ推奨される。相対インポートは、現在のパッケージの内部にあるモジュール間で行われる。
Python
# 標準ライブラリのインポート
import os
import sys

# 空行をはさむ
# サードパーティのライブラリのインポート
from flask import Flask, render_template

# 空行をはさむ
# ローカルアプリケーションで作成したモジュールのインポート
from mypackage import mymodule

__init__.pyファイル

Pythonでディレクトリをパッケージとして認識させるために使用される特別なファイル。

Python 2とPython 3.3未満では、このファイルがパッケージのルートにないと、ディレクトリはパッケージとして認識されなかった。Python 3.3以降では、__init__.pyファイルがなくても、そのディレクトリは名前空間パッケージとして認識されるようになった。

このファイルがあるディレクトリは、インポートの際に上記の初期化コードが実行されるため、パッケージとして機能する。また、__version__などの属性はパッケージに関する情報を提供し、from . import ...行により、サブモジュールがパッケージと共にインポートされる。

パッケージが大規模で複雑になるほど、__init__.pyファイルで行う初期化の重要性が高まる。一方で、特に初期化が必要ない場合は、このファイルを空にしておくことも一般的。

Python
# __init__.py
print("パッケージ名がインポートされました。")

# パッケージのバージョン情報
__version__ = '1.0.0'

# 必要なサブモジュールのインポート
from . import submodule1, submodule2

フォーマッターとLint

autopep8導入:VSCodeでPython自動フォーマッターを設定

autopep8 - Visual Studio Marketplace
Extension for Visual Studio Code - Formatting support for Python files using the autopep8 formatter.

formatterとは、linterとは違い、スペースの数や挿入位置、改行の位置など、プログラムの動作ではなく見た目に関わる内容を自動修正してくれるもの

VSCodeのユーザー設定画面をひらく。

1. Ctrl+Shift+Pキーでコマンドパレットを開いて「settings」と入力し、「基本設定: ユーザー設定を開く (JSON)」をクリックする。

そして、下記を追加する。

editor.formatOnSave の設定を trueにすることでpythonコードが自動フォーマットされるようになる。

Python
"[python]": {
    "editor.defaultFormatter": "ms-python.autopep8",
    "editor.formatOnSave": true
  }

Linter:pylint

linterとは、コードに問題点がないかを確認する静的解析ツールのこと。

vscodeでは、Pylint, flake8, mypyの3つが紹介されていた。今回はflake8を導入

Linting Python in Visual Studio Code
Linting Python in Visual Studio Code

__pycache__ (パイキャッシュ)ディレクトリ

  • __pycache__ ディレクトリは、Pythonインタープリターによって生成されるバイトコードキャッシュファイルが保存されるディレクトリ。Pythonのソースコードは通常のテキストファイルで保存されているが、実行時に高速に読み込むためにPythonはバイトコードと呼ばれる中間表現にコンパイルされる。このバイトコードは通常のPythonソースコードよりも高速に読み込むことができ、またプラットフォームに依存しない形式。
  • flask コマンドを実行すると、app.py という名前のPythonソースコードファイルに対応するバイトコードファイルは __pycache__/app.cpython-<バージョン>-<プラットフォーム>.pyc のような名前で保存される。

.gitignoreに記載

Python
# __pycache__ ディレクトリを無視
__pycache__/

.pyc ファイルはPython 3以降では通常 __pycache__ ディレクトリ内に生成される。しかし、古いバージョンのPythonや、何らかの理由で .pyc ファイルがプロジェクトの他の場所に生成される可能性がある。その場合は、下記のように記載

Python
# Pythonのバイトコンパイルされたファイルを無視
*.pyc

# __pycache__ ディレクトリを無視
__pycache__/

文字フォーマット

Python3.6から、「f文字列(フォーマット済み文字列リテラル:f-string)」が追加された。

f文字列の追加により、format()メソッドを使わなくても、文字列の中に書式を指定して数値などの値を埋め込める。{0}{1}のようにインデックス番号ではなく(省略可能)、変数名で直接指定できるので、直感的で可読性も良くなった。

通常の1行の例

Python
name = "世界"
message = f"こんにちは、{name}!"
print(message)  # こんにちは、世界!

長い文字列(複数行の例)

複数行にわたる文字列では、トリプルクォート(""" または ''')を使用する。

Python
name = "世界"
profession = "開発者"
country = "日本"

long_message = f"""
こんにちは、{name}さん。
あなたの職業は{profession}で、国籍は{country}ですね。
素晴らしいです!
"""

print(long_message)

上記をformat()メソッドを用いて記載すると下記。プレースホルダー {} を文字列に配置し、format() メソッドに渡す引数によって値を埋め込む。各プレースホルダーはformat()メソッドの引数の順番に基づいて値が割り当てられる。

この例では、{}の中にはインデックス番号が省略しおり、format()メソッドに渡された引数が順番に対応する。インデックス番号を使用することもできる。

format()メソッドを使用する場合、プレースホルダーの位置を正確に把握しておく必要があり、引数の順番が重要になる。これに対し、f-stringsは変数名を直接プレースホルダーの中に書くことができる。

Python
name = "世界"
profession = "開発者"
country = "日本"

long_message = """
こんにちは、{}さん。
あなたの職業は{}で、国籍は{}ですね。
素晴らしいです!
""".format(name, profession, country)

print(long_message)

コメント

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