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公式サイトこちら
https://packaging.python.org/ja/latest/guides/installing-using-pip-and-virtual-environments/
仮想環境作成
プロジェクトディレクトリ直下に仮想環境作成する。
$ 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のパッケージ管理システム)などのツールはその時点でのデフォルトのバージョンがインストールされる。これらのツールのバージョンが古い場合は、仮想環境内で手動でアップグレードする必要がある。
pip install --upgrade pip
しかし、これは面倒。Python3.9からは、--upgrade-deps
というオプションが追加された。このオプションをつけて仮想環境を作成すると、pip
などが最新バージョンの状態で仮想環境が作成される。depsはdependenciesの略。頻繁に新しい環境を作成する場合や、常に最新のツールを使いたい場合に便利。
$ python3 -m venv --upgrade-deps [newenvname]
特定のpythonバージョンで仮想環境を作成したい場合
上記python3コマンドで選択されるPythonバージョンは、その時点で python3
コマンドに関連付けられているPythonのバージョンになる。システムのパス(PATH
環境変数)に設定されているPythonのバージョンだったり、システムにインストールされているPythonの中の最新バージョンだったりする。
python3 --version
で表示されるpython version。
python3 -m venv .venv
コマンドで作成された仮想環境内では、その環境が作成された時に使用されたPythonバージョンを後から変更することはできない。
特定のpythonバージョン(たとえばpython3.9)で仮想環境を作成したい場合は、そのpythonバージョン名で仮想環境を作成する。
python3.9 -m venv .venv
上記コマンドを実行するためには、システム(ホストOS)にpython3.9が元々インストールされている必要がある。python3 -m venv .venv を実行して作成された仮想環境下で特定のpython versionをインストールすることはできない。
一方で、pythonライブラリは、Pythonの標準ライブラリ以外に必要なパッケージを pip
を使用してインストールできる。これらのパッケージはその仮想環境に限定され、他の環境やシステム全体には影響しない。
仮想環境の有効化(Activate)
仮想環境を作成しただけでは、まだ仮想環境を利用している状態にはなっていない。仮想環境を有効化する必要がある。
$ source [newenvname]/bin/activate
仮想環境が有効化されると、プロンプトの頭に利用中の仮想環境が()つきで表示される。
pythonののバージョン確認は、--version
オプションまたは-V
オプションを付けて実行。-vではない。
([newenvname])$ python -V
Python 3.12.1
仮想環境を有効化しても、それはそのコマンドプロントやターミナルに限られる。システム全体の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はバージョン指定なしで最新バージョンをインストール(バージョンを明記しない場合)する。
flask==1.1.2
requests>=2.24.0
nump
仮想環境の無効化(Deactivate)
非常にシンプル。仮想環境を有効化した時に利用できるコマンド。
これで、プロンプトに付いていた([newenvname)
が無くなり、仮想環境が無効化される。
([newenvname])$ deactivate
$
Dockerを使用する際には、source env/bin/activate
を実行して仮想環境をアクティブにする必要は基本的にはない。Dockerは、アプリケーションとその依存関係をコンテナ化するためのツールであり、その環境はホストマシンの環境(仮想環境を含む)から独立しているため。
Dockerコンテナ内で動作するアプリケーションの依存関係は、主にDockerfile
によって定義される。Dockerfile
は、コンテナ内で必要なソフトウェアのインストール、環境設定、実行コマンドなどを記述するもので、このファイルに基づいてDockerイメージが作成される。
Flaskでソースの変更を検知して、Webアプリを自動リロードする方法
- Flaskをデバッグモードで起動する。main関数に
app.run(debug=True)
とするだけ。これにより、コードに変更があった際にサーバーが自動で再起動し、エラー発生時に詳細なトレースバック(遡及:時系列をさかのぼって記録をたどる)を提供する。
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はそのディレクトリをパッケージとして扱う。
具体例
以下のようなディレクトリ構造がある場合
project/
│
├── main.py
│
└── mypackage/
├── __init__.py
├── module1.py
└── subpackage/
├── __init__.py
└── module2.py
project
はプロジェクトのルートディレクトリ。mypackage
はPythonのパッケージ。これは__init__.py
ファイルが含まれているため。module1.py
とsubpackage
は、mypackage
の中にある。subpackage
もまたパッケージ(自身の__init__.py
ファイルを持っている)。module2.py
はsubpackage
内のモジュール。
相対インポートの例
module1.py
からmodule2.py
をインポートする:module2.py
からmodule1.py
をインポートする:
実行ファイルと相対インポート
main.py
のようなスクリプト(実行ファイル)からは、相対インポートを使うことはできない。main.py
でmypackage
内のモジュールをインポートする場合は、絶対インポートを使用する。
from mypackage import module1
from mypackage.subpackage import module2
# from .mypackage import module1 は不可。不正な相対インポート
pep8によるimport規約
- 標準ライブラリ→3rd partyライブラリ→ローカルアプリケーション/ライブラリ固有のインポートの順に1行ずつあけて書く。偉いライブラリ順
- 複雑なパッケージ構造がない場合には、絶対インポートが推奨されています。これは、絶対インポートの方が読みやすく、通常はより明確なため。
- 相対インポートは、主に複雑なパッケージ内部でのみ推奨される。相対インポートは、現在のパッケージの内部にあるモジュール間で行われる。
# 標準ライブラリのインポート
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
ファイルで行う初期化の重要性が高まる。一方で、特に初期化が必要ない場合は、このファイルを空にしておくことも一般的。
# __init__.py
print("パッケージ名がインポートされました。")
# パッケージのバージョン情報
__version__ = '1.0.0'
# 必要なサブモジュールのインポート
from . import submodule1, submodule2
フォーマッターとLint
autopep8導入:VSCodeでPython自動フォーマッターを設定
https://marketplace.visualstudio.com/items?itemName=ms-python.autopep8
formatterとは、linterとは違い、スペースの数や挿入位置、改行の位置など、プログラムの動作ではなく見た目に関わる内容を自動修正してくれるもの
VSCodeのユーザー設定画面をひらく。
1. Ctrl+Shift+Pキーでコマンドパレットを開いて「settings」と入力し、「基本設定: ユーザー設定を開く (JSON)」をクリックする。
そして、下記を追加する。
editor.formatOnSave
の設定を true
にすることでpythonコードが自動フォーマットされるようになる。
"[python]": {
"editor.defaultFormatter": "ms-python.autopep8",
"editor.formatOnSave": true
}
Linter:pylint
linterとは、コードに問題点がないかを確認する静的解析ツールのこと。
vscodeでは、Pylint, flake8, mypyの3つが紹介されていた。今回はflake8を導入
https://code.visualstudio.com/docs/python/linting
__pycache__ (パイキャッシュ)ディレクトリ
__pycache__
ディレクトリは、Pythonインタープリターによって生成されるバイトコードキャッシュファイルが保存されるディレクトリ。Pythonのソースコードは通常のテキストファイルで保存されているが、実行時に高速に読み込むためにPythonはバイトコードと呼ばれる中間表現にコンパイルされる。このバイトコードは通常のPythonソースコードよりも高速に読み込むことができ、またプラットフォームに依存しない形式。flask
コマンドを実行すると、app.py
という名前のPythonソースコードファイルに対応するバイトコードファイルは__pycache__/app.cpython-<バージョン>-<プラットフォーム>.pyc
のような名前で保存される。
.gitignoreに記載
# __pycache__ ディレクトリを無視
__pycache__/
.pyc
ファイルはPython 3以降では通常 __pycache__
ディレクトリ内に生成される。しかし、古いバージョンのPythonや、何らかの理由で .pyc
ファイルがプロジェクトの他の場所に生成される可能性がある。その場合は、下記のように記載
# Pythonのバイトコンパイルされたファイルを無視
*.pyc
# __pycache__ ディレクトリを無視
__pycache__/
文字フォーマット
Python3.6から、「f文字列(フォーマット済み文字列リテラル:f-string)」が追加された。
f文字列の追加により、format()メソッドを使わなくても、文字列の中に書式を指定して数値などの値を埋め込める。{0}
や{1}
のようにインデックス番号ではなく(省略可能)、変数名で直接指定できるので、直感的で可読性も良くなった。
通常の1行の例
name = "世界"
message = f"こんにちは、{name}!"
print(message) # こんにちは、世界!
長い文字列(複数行の例)
複数行にわたる文字列では、トリプルクォート("""
または '''
)を使用する。
name = "世界"
profession = "開発者"
country = "日本"
long_message = f"""
こんにちは、{name}さん。
あなたの職業は{profession}で、国籍は{country}ですね。
素晴らしいです!
"""
print(long_message)
上記をformat()
メソッドを用いて記載すると下記。プレースホルダー {}
を文字列に配置し、format()
メソッドに渡す引数によって値を埋め込む。各プレースホルダーはformat()
メソッドの引数の順番に基づいて値が割り当てられる。
この例では、{}
の中にはインデックス番号が省略しおり、format()
メソッドに渡された引数が順番に対応する。インデックス番号を使用することもできる。
format()
メソッドを使用する場合、プレースホルダーの位置を正確に把握しておく必要があり、引数の順番が重要になる。これに対し、f-stringsは変数名を直接プレースホルダーの中に書くことができる。
name = "世界"
profession = "開発者"
country = "日本"
long_message = """
こんにちは、{}さん。
あなたの職業は{}で、国籍は{}ですね。
素晴らしいです!
""".format(name, profession, country)
print(long_message)
コメント