1. ホーム
  2. python

__init__ を呼び出さずにクラスをインスタンス化する方法はありますか?

2023-10-27 04:14:57

質問

コンストラクタの __init__ を回避する方法はありますか?

例です。

class A(object):    
    def __init__(self):
        print "FAILURE"

    def Print(self):
        print "YEHAA"

ここで A . これは次のように見えますが、この構文は正しくありません。

a = A
a.Print()

EDITです。

さらに複雑な例です。

オブジェクトがあるとします C があり、その目的は1つのパラメータを保存し、それを使って計算を行うことです。しかし、パラメータはそのまま渡されるのではなく、巨大なパラメータファイルに埋め込まれています。それは次のようなものです。

class C(object):
    def __init__(self, ParameterFile):
        self._Parameter = self._ExtractParamterFile(ParameterFile)
    def _ExtractParamterFile(self, ParameterFile):
        #does some complex magic to extract the right parameter
        return the_extracted_parameter

次に、そのオブジェクトのインスタンスをダンプしてロードしたいと思います。 C . しかし、このオブジェクトをロードするとき、私は単一の変数 self._Parameter という変数しかなく、パラメータファイルを期待しているためコンストラクタを呼び出すことができません。

    @staticmethod
    def Load(file):
        f = open(file, "rb")
        oldObject = pickle.load(f)
        f.close()

        #somehow create newObject without calling __init__
        newObject._Parameter = oldObject._Parameter
        return newObject

つまり、パラメータファイルを渡さずにインスタンスを作成することはできないのです。しかし、私の場合、それはパラメータファイルではなく、メモリ上に持ち運んだり、ディスクに保存したりしたくないような巨大なデータのゴミです。

のインスタンスを返したいので C のインスタンスを返したいので Load どうにかしてコンストラクタを呼び出す必要があります。

OLD EDITです。

より複雑な例で、説明すると なぜ という質問をしているのです。

class B(object):    
    def __init__(self, name, data):
        self._Name = name
        #do something with data, but do NOT save data in a variable

    @staticmethod
    def Load(self, file, newName):
        f = open(file, "rb")
        s = pickle.load(f)
        f.close()

        newS = B(???)
        newS._Name = newName
        return newS

見ての通り data はクラス変数に格納されていないので、これを __init__ . もちろん、単純に格納することもできますが、データが巨大なオブジェクトで、常にメモリ上に持ち歩くことも、ディスクに保存することもしたくない場合はどうすればよいでしょうか?

どのように解決するのですか?

あなたは ができます。 回避する __init__ を呼び出すことで __new__ を直接呼び出します。そして、指定された型のオブジェクトを作成し、代替のメソッドとして __init__ . これは、何かというと pickle はそうするでしょう。

しかし、まず非常に強調したいのは、それはあなたが はいけません。 を行い、あなたが何を達成しようとしているにせよ、より良い方法があり、そのうちのいくつかは他の回答で言及されています。特に 悪い を呼び出すのをスキップするのは悪い考えです。 __init__ .

オブジェクトが作成されるとき、多かれ少なかれこのようなことが起こります。

a = A.__new__(A, *args, **kwargs)
a.__init__(*args, **kwargs)

2番目のステップをスキップすることができます。

ここで、なぜあなたが をしてはいけないのか を行う必要があります。 の目的は __init__ はオブジェクトを初期化し、すべてのフィールドを埋め、そして確実に __init__ メソッドが呼び出されるようにします。とは pickle では、オブジェクトに関連するすべてのデータを保存しようとするので例外です ( 任意の フィールド/インスタンス変数を含む) を保存しようとするので、例外となります。 __init__ によって設定されたものはpickleによって復元されるので、再度呼び出す必要はありません。

もし __init__ をスキップして別のイニシャライザを使用すると、一種のコードの重複が発生します。インスタンス変数が埋められる場所が2つあり、イニシャライザの1つでその1つを見逃したり、誤って2つのフィールドを異なる動作にすることは容易いことです。このため、微妙なバグが発生する可能性があり、それを追跡するのはそれほど簡単ではありません。 を知る必要があります。 を知る必要があります)、コードのメンテナンスがより困難になります。言うまでもなく、継承を使用している場合はさらに大きな混乱に陥ります。問題は継承の連鎖を上がっていくことになり、この代替の初期化子を連鎖の上のあらゆる場所で使用しなければならないからです。

また、そうすることで、Pythonのインスタンス生成をオーバーライドして、独自のものを作ることになります。Pythonはすでにあなたのためにそれをうまく行っており、それを再発明する必要はありませんし、あなたのコードを使用する人々を混乱させるでしょう。

代わりに何をするのがベストか、ここにあります。 単一の __init__ メソッドを使用します。このメソッドは、クラスのすべてのインスタンス化に対して呼び出され、すべてのインスタンス変数を適切に初期化します。初期化の異なるモードでは、2つのアプローチのどちらかを使用します。

  1. の異なるシグネチャをサポートする。 __init__ をサポートし、オプションの引数を使用することでケースを処理します。
  2. 代替コンストラクタとして機能する、いくつかのクラスメソッドを作成します。それらがすべて通常の方法でクラスのインスタンスを作成することを確認します。 __init__ ), は、Roman Bodnarchuk が示したように のように、追加作業や何かを実行しながら すべてのデータをクラスに渡すとよいでしょう(そして __init__ がそれを処理する)のがベストですが、それが不可能だったり不便だったりする場合は、インスタンスが生成された後にいくつかのインスタンス変数を設定し、そのインスタンスを __init__ が初期化を終えた後に、いくつかのインスタンス変数を設定することができます。

もし __init__ がオプションのステップを持つ場合 (たとえば data を処理するようなもの)、それをオプションの引数にするか、処理を行う通常のメソッドを作るか...あるいはその両方が可能です。