2017-11-03 3 views
1

私はウォークアラウンドしようとしているreload()(Python 2の組み込み版とimportlibの両方)の厄介な動作を発見しました。super()をエレガントで安全な方法(Pythonモジュールの再読み込みに関して)でどのように使うことができますか?

私はインタラクティブなPythonインタプリタでデータを分析しています。私のコードは、よく変更されるモジュール(Python 2と3の両方に対応しています)で構成されています。

データを読み込む時間が長いため、インタープリタを再起動できないため、代わりにモジュールを再帰的に読み込むことをお勧めします。

問題はreload() updates the code but preserves the module global scopePython 3 importlib.reload()にも適用されます)です。 super()を使用する方法には有害であると思われます(何が起こっているかを理解するのにはしばらく時間がかかりました)。

モジュールbar.pyための最小限の失敗例:

class Bar(object): 
    def __init__(self): 
     super(Bar, self).__init__() 

である:

>>> import bar 
>>> class Foo(bar.Bar): 
...  pass 
... 
>>> reload(bar) 
<module 'bar' from '[censored]/bar.py'> 
>>> Foo() 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
    File "[censored]/bar.py", line 3, in __init__ 
    super(Bar, self).__init__() 
TypeError: super(type, obj): obj must be an instance or subtype of type 

I月:Pythonの2と互換性 ない

  1. use super() without arguments in Python 3 manner( )、
  2. それを放棄する代わりにBar.__init__(self)which is harder to maintaindiscouraged)を呼び出してください。
  3. monkey-patchクラスを追加したクラス属性を追加して、クラス自体に循環 の参照を追加しました。

私が好きなアイデアはありません。問題に対処する他の方法はありますか?

+1

あなたの 'bar.py'は、' __init__'メソッドの代わりに 'super(Bar、self).__ init __()'をクラスレベルに置いているので既に失敗しています。 – user2357112

+0

'super 'の使用は実際には無効です。 'super'はクラス本体にないクラスのメソッド内で使われます。 – direprobs

+1

また、モジュールをリロードすることは、一般的に正しく行うのが難しく、 'reload'は非常に単純なケースしか処理しません。最善の策は常にPythonを最初から再起動することです。 – user2357112

答えて

1

モジュールの再読み込みに関しては、エレガントで安全に何かを行うことは基本的に不可能なので、できません。そのようなことは正しければ超難しいです。

という問題がここに明示された特定の方法は、Fooのスーパークラスがまだ古いBarであるということですが、古いBarは、名前で自身を指し、そしてBar名前が、今では見ている名前空間に新しいBarを指し、 。古いBarは新しいBarsuperに渡そうとしています。


いくつかのオプションがあります。それらのすべてはです。安全でないおそらくあなたに奇妙な驚きを与えるでしょう。最終的にはですが、モジュールリロードについても同様です。

おそらく最も簡単に理解オプションは、新しいBarから降順新しいFooクラスを取得するためにFooクラス定義を再実行することです:Foo

reload(bar) 
class Foo(bar.Bar): 
    pass 

新しいインスタンスは、新しいBarを使用し、そして意志ますsuperの問題は発生しません。古いインスタンスは古いFooと古いBarを使用します。 __init__で既に問題は発生していないため、問題は発生しませんが、他の問題が発生する可能性があります。 更新Fooのスーパークラスへ

あなたが取ることができる別のオプションがされのでFooは今、新しいBarから下る:

reload(bar) 

Foo.__bases__ = (bar.Bar,) 

新とFooの古いインスタンスは、新しいBarを使用します。 Barで変更した内容によっては、新しいクラスが古いインスタンスと互換性がない場合があります。特に、Barのデータを変更した場合、またはBar.__init__が初期化を実行する方法を変更した場合。

+0

実際のサンプルモジュールでは、インポートを再帰的に再読み込みしているので、コードの違いがないことを願っています。 – abukaj

0

あなたが本当にしたいようなサウンドを再importモジュール。以下のことが可能です(Python 2とPython 3の両方)。

import bar 
import sys 

class Foo(bar.Bar): 
    pass 

# Manually reload module. 
_saved_reference = sys.modules['bar'] # Needed for Python 2. 
del sys.modules['bar'] 

import bar # Re-import instead of reload(). 
Foo() # No error. 
+0

'_saved_reference'とは何ですか? – user2357112

+0

このソリューションには奇妙な驚きがあります。 'bar'と' Bar'への古い参照は古いモジュールと古いクラスを参照するので、 'Foo'はまだ古いクラスを使用し、' bar'をインポートした他のモジュールは古いモジュール。このようにパッケージを再インポートすると、サブモジュールへのアクセスが中断される可能性もあります。 Pythonを再起動しなければ、どんなオプションも奇妙な驚きを起こすでしょう。 – user2357112

+0

@ user2357112:Python 2のために '_saved_reference'が必要です。私の[この質問](https://stackoverflow.com/questions/5365562/why-is-the-value-of-name-changing-after-assignment-to-sys-modules-name)に受け入れられている回答を参照してくださいそれについて議論する。 – martineau

関連する問題