Pythonで環境変数(os.environ)を使う: 取得、追加、更新、削除

271, 2021-06-16

目次

Pythonで環境変数を使う

Pythonでは標準ライブラリのos.enrvionを使うと、環境変数にアクセスすることができます。
特定の環境変数の値を取得したり、設定したりすることが可能です。

結論から言うと、環境変数は↓のように設定/取得します。

import os


os.environ['MYVAR'] = 'hello'

myvar = os.environ['MYVAR']
print(myvar)
# hello

この記事ではos.environを使った環境変数の設定について具体的に↓を見ていきます。

  • 環境変数とは?

  • os.envrionとは?

  • 環境変数の取得

  • 環境変数の設定

  • 環境変数の更新

  • 環境変数の削除

環境変数とは?

環境変数とはプロセスに設定できる変数のことを言います。

プロセスとは、アプリケーションを起動したときに作成されるアプリケーションの単位となるものです。
アプリケーションを起動することとプロセスを起動することはほぼ同じ意味です。
プロセス内でメインスレッドが起動し、開発者がスクリプトなどに書いたコードが実行されます。

環境変数はプロセス内から設定/参照することができます。
また、子プロセスに親プロセスの環境変数を引き継がせることも出来ます。

基本的にはプロセス内の環境変数の変更は、そのプロセス内に限定されます。
Pythonのスクリプトを走らせるとプロセスが起動しますが、そのプロセスからシステム全体の環境変数を変えることはできません。
例外としてそのスクリプトから新しく起動する子プロセスにおいては、親プロセスであらかじめ環境変数を設定しておくことで切り替えができます。

os.envrionとは?

os.environとは環境変数が保存されている辞書のことです。

os.environosモジュールをインポートすると使うことができます。

import os

os.environos._Environ型の辞書です。

print(type(os.environ))
# <class 'os._Environ'>

print(isinstance(os._Environ, dict))
# False

辞書とは言いますが、辞書のようなインターフェースを持っているだけであって↑のようにdictを継承しているわけではありません。

os.environには環境変数が保存されており、ここに保存されている環境変数を変更したり新しく加えたりすることで現在のプロセスの環境変数を変更することができます。
この辞書に保存できるのは文字列のみになっています。

環境変数の取得

os.environから環境変数を取得するには↓のように角カッコにキーを渡してアクセスします。

myvar = os.environ['MYVAR']
print(myvar)
# hello

↑の場合、↓のようにスクリプトを起動している場合はmyvarには「hello」と言う文字列が代入されます。

$ MYVAR=hello python script.py
hello

キーが存在しない場合は例外KeyErrorが送出されます。

try:
    os.environ['NOTHING']
except KeyError as e:
    print(e)
    # 'NOTHING'

↓のようにget()メソッドを使うことも出来ます。

myvar = os.environ.get('MYVAR')
print(myvar)
# hello

get()を使う場合は第2引数にデフォルト値を指定することができます。

myvar = os.environ.get('NOTHING', 'world')
print(myvar)
# world

↑の場合、NOTHINGは存在しない環境変数ですが、その値の代わりにworldget()から返されます。

環境変数の設定

os.environに環境変数を設定するには↓のようにキーに対して代入を行います。

os.environ['MYVAR'] = 'hello'

print(os.environ['MYVAR'])
# hello

os._Environのメソッド__setitem__が呼ばれ、内部的にはputenv()が呼ばれます。
すでにキーの値がセットされている場合は加算代入も行えます。

os.environ['MYVAR'] += ' world'

print(os.environ['MYVAR'])
# hello world

文字列以外の値を代入しようとすると例外TypeErrorが送出されます。

try:
    os.environ['MYVAR'] = 1
except TypeError as e:
    print(e)
    # str expected, not int

環境変数の更新

os.environに設定されている環境変数を更新するには設定の時と同じように角カッコでキーを指定して代入します。

os.environ['MYVAR'] = 'hello'
print(os.environ['MYVAR'])
# hello

update()メソッドを使っても更新することができます。この場合は引数のキーがそのままキーに、値が値になります。

os.environ.update(MYVAR='world')
print(os.environ['MYVAR'])
# world

update()を使う場合は一度に複数の環境変数を更新できます。

os.environ.update(AAA='This is AAA', BBB='This is BBB')
print(os.environ['AAA'])
# This is AAA

print(os.environ['BBB'])
# This is BBB

環境変数の削除

os.environから環境変数を削除するにはdelキーワードを使います。

os.environ['MYVAR'] = 'hello'
print(os.environ['MYVAR'])
# hello

del os.environ['MYVAR']
print(os.environ.get('MYVAR', 'nothing'))
# nothing

↑の場合、MYVARという環境変数を設定していますが、そのあとにdelMYVARを削除しています。
そのためget()MYVARを参照できず、デフォルト値のnothingが返されます。
os._Environのメソッド__delitem__()が呼ばれ、内部的にはunsetenv()が呼ばれます。

存在しない環境変数にdelを使った場合はKeyErrorになります。

try:
    del os.environ['NOTHING']
except KeyError as e:
    print(e)
    # 'NOTHING'

↑の場合はNOTHINGの参照時点でKeyErrorが発生しています。そのためdel自体は実行されていません。

またpop()メソッドを使っても環境変数を削除することができます。

os.environ['MYVAR'] = 'hello'

myvar = os.environ.pop('MYVAR')
print(myvar)
# hello

print('MYVAR' in os.environ.keys())
# False

↑の場合MYVARという環境変数を設定しています。
それをpop()で取得して同時に削除します。
'MYVAR' in os.environ.keys()os.environの中にキーが含まれているか確認すると、結果はFalseになります。
つまりMYVARという環境変数が削除されたことがわかります。

ほかにはclear()を使っても環境変数を削除できます。
clear()は辞書の中身を空にします。

print(len(os.environ.keys()))
# 35

os.environ.clear()

print(len(os.environ.keys()))
# 0

↑の場合os.environのキーの数は最初は35ありましたが、clear()後は0になっているのがわかります。

マルチスレッドで環境変数を使う

環境変数はプロセスに設定されます。
そのためマルチスレッドなプログラムにおいても環境変数を参照することができます。

import os
import threading
import time


def func1():
    time.sleep(5)
    os.environ['STATUS'] = 'stop'
    print('func1 done')


def func2():
    while True:
        time.sleep(1)
        print('func2 running')
        if os.environ.get('STATUS', 'running') == 'stop':
            break

    print('func2 done')


t1 = threading.Thread(target=func1)
t2 = threading.Thread(target=func2)

t1.start()
t2.start()
t1.join()
t2.join()

↑のコードを実行すると↓のような結果になります。

func2 running
func2 running
func2 running
func2 running
func1 done
func2 running
func2 done

↑の場合、func1()func2()がスレッドの関数として起動します。
そしてfunc1()5秒後に環境変数STATUSの値を設定します。
func2()ではその環境変数STATUSの値を監視して、値がstopになったら無限ループから脱出しています。

ただこのようなコードは一応書けますが、あまり一般的ではないかもしれません。
普通はただの変数とミューテックスなどの排他的制御処理を行います。
またthreadingモジュールにはLockがあるのでこれを使うのがより一般的と言えます。

またスレッドセーフ的にどうかというところですが、ソースコードを見たところos._Environの各種メソッドはデータにアクセスする際に排他的制御を行っていないようです。
そのため↑のコードは意図しない動作になる可能性もあります。
ただPythonにはGIL(グローバルインタプリタロック)が備わっているため、大きく問題化することはもしかしたらないのかもしれません。
ですが、マルチスレッドプログラミングにおいては排他的制御はした方が良いのが通常かと思われます。
そのため実装する場合は排他的制御を検討してみてください。

おわりに

今回はPythonの環境変数について見てみました。
環境変数は慣れると非常に便利な仕組みです。サーバーの管理が楽になったりするのでぜひブックマークお願いします。

環境変数のせい

環境変数が良かった