sys.modules[key]にclassインスタンスを代入して複数回それをimportしてもインスタンス初期化されないことを確認した。
2回importしたら1回目の属性設定が消えるのでは?という懸念があったが、同一モジュールは1回しかimportされず問題なかった。
$ python 0.py
Pythonでreadonlyな定数を作りたい。
const.py
class _const:
class ConstError(TypeError): pass
def __setattr__(self,name,value):
if name in self.__dict__.keys(): raise self.ConstError('readonly。再代入禁止。')
self.__dict__[name]=value
- objectクラスの
__setattr__
メソッドを継承する(属性を設定するときに実行される) __setattr__
で属性名が既存ならエラーにする
_const
クラスのインスタンス属性に2度目の代入をしようとすると例外発生する。
inst = _const()
inst.attr = 'a'
inst.attr = 'a' # ConstError
以下の問題が残るが、以降で解決する。
- インスタンスを生成せねばならないのが面倒
- 全体で1つだけにしたい
const.py
class _const: ...
import sys
sys.modules[__name__]=_const()
sys.modules[__name__]=_const()
で、const.py
モジュールに_const
クラスのインスタンスを代入している。
0.py
import const
print(const, type(const)) #<const._const object at 0xb712932c> <class 'const._const'>
$ python 0.py
で実行すると、上記の通り、モジュールにclassインスタンスが代入される。
もしconstがモジュールなら、以下のように表示されていたはずである。
<module 'const' from '/.../const.py'> <class 'module'>
sys.modules
の操作はPython文書によると、真っ当ではないらしい。でも今回のようなトリック操作ができる。
既に読み込まれているモジュールとモジュール名がマップされている辞書です。これを使用してモジュールの強制再読み込みやその他のトリック操作が行えます。ただし、この辞書の置き換えは想定された操作ではなく、必要不可欠なアイテムを削除することで Python が異常終了してしまうかもしれません。
import const
const.attr = 'value'
const.attr = 'value'
2の通り、constはモジュールではなく、const._const
クラスのインスタンスである。
1の通り、2度めの代入で例外が発生する。
これにてreadonlyな定数の実装が可能となる。間違って既存の名前で代入しようとするとエラーになる。
ただ、新規生成(定義)は1つのファイル内でやらないと名前重複が探しづらくなる。別々のファイルで定義すると、どのファイルで定義しているか見つけられなくなる。
以下の疑問が生じたので試した。結果、問題なかった。
2回importしたら2回sys.modules[__name__]=_const()
が実行されてしまうのではないか?
_const()
によりインスタンスは新しいものが生成され、=
式で代入され、これまでの_const
インスタンスとそれに代入していた属性は消えてしまうのでは?
const.py
print('***** import start const.py ****')
...
import sys
sys.modules[__name__]=_const()
print('***** import end const.py ****')
0.py
import const
...
import const
***** import start const.py ****
は1回しか表示されなかった
0.py
import const
const.test = "Test"
import const
const.test = "Test"
- 2回目のimport文のあとで、その前に作った属性に代入すると例外が発生した
思い通りに動作している。上記の結果から「同一moduleは1回しかimport
されない」ということなのか?
import
文の回数だけ実行されるのでは?
以下の検証でも1回のみだった。
- main.py
- const.py
- sub.py
main.py
import sub
import const
print(const, type(const))
import const
sub.py
import const
const.py
print('CONST!')
- 同一module内で2回、同一moduleをimportする
- 異なるmoduleをまたいで同一moduleをimportする
実行しても、CONST!
の表示は1回だけだった。
$ python main.py
CONST!
「同一mouduleは1回しかimportされない」ということだろう。
- Linux Mint 17.3 MATE 32bit
- pyenv 1.0.10
- Python 3.6.1
Library | License | Copyright |
---|---|---|
http://code.activestate.com/recipes/65207/ | PSF | Copyright (c) 2001 Python Software Foundation; All Rights Reserved |