Pythonで作成したexeのために、テキストファイルで変数を定義したい


# 目的
以下のようなテキストファイルを読み込んで、その中の記載通りに変数を定義したい。
たとえば、USERNAMEという変数に、usernameという文字列を入れるなど。

```text:input.txt
USERNAME = username
PASSWORD = password
```

前提として、最終的にpythonファイルはexe化して配布し、個々人のパラメータをテキストファイルに記載して読み込ませようという魂胆。

# 試み
最もスタンダードなのは、以下のようなpyファイルを`from input import *`してしまうことだが、これだと、exe化するときに一緒にバイナリ化されてしまうため、目的を達成できない。

```py:input.py
USERNAME = 'username'
PASSWORD = 'password'
```

一番最初に思いついたやり方は以下だが、変数分記載が必要で面倒。

```py3
with open('input.txt') as f:
    for line in f.readlines():
        if 'USERNAME' in line:
            USERNAME = line.split()[2]
```

# 回答
localsという組み込み関数を使うとよい。localsは変数の出力として紹介されているケースが多いが、定義自体にも使える。
__コメントで指摘をいただきましたが、localsの内容は更新してはいけないようです。代わりにglobalsを使用したほうがよいとのこと。__

```py:元々のアイディア
with open('input.txt') as f:
    for line in f.readlines():
        locals()["{0:s}".format(line.split()[0])] = line.split()[2]
        
```

```py:修正したもの
with open('input.txt') as f:
    for line in f.readlines():
        globals()["{0:s}".format(line.split()[0])] = line.split()[2]
        
```

# locals()とglobals()について
ご指摘いただいた後に、ちゃんと理解できていなかったので、改めて調べた結果。

```py:test.py
def test1():
    a = 1
    b = 2
    local = locals()
    print(local)
    print(locals())
    c = 3
    print(local)
    print(locals())


def test2():
    a = 1
    b = 2
    print(locals())
    locals()['a'] = 4
    print(locals())


print('test1. locals()は特定の関数(名前空間)内で、その瞬間の変数を参照している。')
print('いったん変数に書き出すと、それは更新されない(当然だけど)')
test1()
print('test2. locals()は名前空間を参照したものなので、直接更新しても、変数の値は更新されない。')
test2()

print('モジュールの一番上のレベル(?)で呼び出した場合、実質globals()と同じになる。')
print('globals()は名前空間を参照したものではなく、名前空間そのものなので。')
a = 1
b = 2
print(locals())
locals()['a'] = 4
print(locals())
```

出力結果は、

```shell-session
test1. locals()は特定の関数(名前空間)内で、その瞬間の変数を参照している。
いったん変数に書き出すと、それは更新されない(当然だけど)
{'b': 2, 'a': 1}
{'b': 2, 'a': 1, 'local': {...}}
{'b': 2, 'a': 1, 'local': {...}}
{'b': 2, 'a': 1, 'local': {...}, 'c': 3}
test2. locals()は名前空間を参照したものなので、直接更新しても、変数の値は更新されない。
{'b': 2, 'a': 1}
{'b': 2, 'a': 1}
モジュールの一番上のレベル(?)で呼び出した場合、実質globals()と同じになる。
globals()は名前空間を参照したものではなく、名前空間そのものなので。
{'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x013E0C10>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, '__file__': 'C:/Users/ikedak2/PycharmProjects/AttendancePro/test.py', '__cached__': None, 'test1': <function test1 at 0x0338CFA8>, 'test2': <function test2 at 0x03398078>, 'a': 1, 'b': 2}
{'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x013E0C10>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, '__file__': 'C:/Users/ikedak2/PycharmProjects/AttendancePro/test.py', '__cached__': None, 'test1': <function test1 at 0x0338CFA8>, 'test2': <function test2 at 0x03398078>, 'a': 4, 'b': 2}

```

# 改善
__この項目もlocalsを使用していたので、globals()に修正__
本当はインプットファイルをjsonなどで用意したほうがスマートか。

```json:input.json
{
 "USERNAME" : "username",
 "PASSWORD" : "password",
}
```

コードは以下。

```py
with open('input.json', 'r') as f:
    input = json.load(f)

for entry in input.keys():
    globals()["{0:s}".format(entry)] = input[entry]
```