Skip to content

sys.modules[key]にclassインスタンスを代入して複数回それをimportしてもインスタンス初期化されないことを確認した。

License

Notifications You must be signed in to change notification settings

ytyaru/Python.Const.201709140800

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 

Repository files navigation

このソフトウェアについて

sys.modules[key]にclassインスタンスを代入して複数回それをimportしてもインスタンス初期化されないことを確認した。

2回importしたら1回目の属性設定が消えるのでは?という懸念があったが、同一モジュールは1回しかimportされず問題なかった。

前回

実行

$ python 0.py

問題

Pythonでreadonlyな定数を作りたい。

解法

1. __setattr__で再代入禁止する

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
  1. objectクラスの__setattr__メソッドを継承する(属性を設定するときに実行される)
  2. __setattr__で属性名が既存ならエラーにする

_constクラスのインスタンス属性に2度目の代入をしようとすると例外発生する。

inst = _const()
inst.attr = 'a'
inst.attr = 'a' # ConstError

以下の問題が残るが、以降で解決する。

  • インスタンスを生成せねばならないのが面倒
  • 全体で1つだけにしたい

2. moduleにclassインスタンスを代入する

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 が異常終了してしまうかもしれません。

3. classインスタンスの属性を動的生成する

import const
const.attr = 'value'
const.attr = 'value'

2の通り、constはモジュールではなく、const._constクラスのインスタンスである。

1の通り、2度めの代入で例外が発生する。

これにてreadonlyな定数の実装が可能となる。間違って既存の名前で代入しようとするとエラーになる。

ただ、新規生成(定義)は1つのファイル内でやらないと名前重複が探しづらくなる。別々のファイルで定義すると、どのファイルで定義しているか見つけられなくなる。

今回の本題

以下の疑問が生じたので試した。結果、問題なかった。

2回importしたら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文のあとで、その前に作った属性に代入すると例外が発生した

同一mouduleは1回しか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

About

sys.modules[key]にclassインスタンスを代入して複数回それをimportしてもインスタンス初期化されないことを確認した。

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages