web-dev-qa-db-ja.com

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

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

例:

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

    def Print(self):
        print "YEHAA"

ここで、Aのインスタンスを作成したいと思います。このように見えますが、この構文は正しくありません。

a = A
a.Print()

編集:

さらに複雑な例:

オブジェクト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のインスタンスを返したいので、どういうわけかコンストラクタを呼び出す必要があります。

古い編集:

whyを説明するより複雑な例:私は質問をしています:

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__に渡すことはできません。もちろん、単純に保存することもできますが、データが巨大なオブジェクトであり、常にメモリに持ち込んだり、ディスクに保存したくない場合はどうでしょうか?

56
Woltan

can__init__を直接呼び出すことにより、__new__を回避します。次に、指定されたタイプのオブジェクトを作成し、__init__の代替メソッドを呼び出すことができます。これは、pickleが行うことです。

しかし、最初に、それはあなたshould n'tやあなたが何でもすることだと強調したい達成しようとすると、より良い方法がありますが、そのいくつかは他の回答で言及されています。特に、__init__の呼び出しをスキップするのはbadのアイデアです。

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

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

2番目のステップはスキップできます。

ここにあなたがshould n'tする理由があります:__init__の目的はオブジェクトを初期化し、すべてのフィールドを指定し、親クラスの__init__メソッドも呼び出されるようにします。 pickleでは、オブジェクトに関連付けられたすべてのデータ(オブジェクトに設定されているanyフィールド/インスタンス変数を含む)を保存しようとするため、例外です。 __init__によって設定され、前回はpickleによって復元されるため、再度呼び出す必要はありません。

__init__をスキップして別の初期化子を使用すると、コードの重複が発生します。インスタンス変数が入力される場所は2つあり、そのうちの1つを簡単に見落とす可能性があります。イニシャライザを使用したり、誤って2つのフィールドを塗りつぶしたりすると、フィールドの動作が異なります。これにより、トレースするのがそれほど簡単ではない微妙なバグが発生する可能性があり(どの初期化子が呼び出されたかknowする必要があります)、コードの保守がより難しくなります。継承を使用している場合はさらに混乱することは言うまでもありません-この代替イニシャライザをチェーンのどこでも使用する必要があるため、問題は継承チェーンに行きます。

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

代わりに最適な方法は次のとおりです:すべてのインスタンスを初期化するクラスのすべての可能なインスタンス化のために呼び出される単一の__init__メソッドを使用します変数を適切に。初期化のさまざまなモードでは、2つのアプローチのいずれかを使用します。

  1. オプションの引数を使用してケースを処理する__init__のさまざまな署名をサポートします。
  2. 代替コンストラクターとして機能するいくつかのクラスメソッドを作成します。それらがすべて、通常の方法でクラスのインスタンスを作成することを確認してください(つまり、__init__を呼び出す)、 Roman Bodnarchukが示すように 、追加の作業などを実行します。クラスにすべてのデータを渡す(および__init__が処理する)のが最善ですが、それが不可能または不便な場合は、インスタンスを作成して__init__の初期化が完了した後にインスタンス変数を設定できます。

__init__にオプションのステップがある場合(たとえば、data引数の処理のように、より具体的にする必要があります)、オプションの引数にするか、通常のメソッドを作成して処理中...またはその両方。

54
Rosh Oxymoron

classmethodメソッドにLoadデコレーターを使用します。

class B(object):    
    def __init__(self, name, data):
        self._Name = name
        #store data

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

        return cls(newName, s)

だからあなたができる:

loaded_obj = B.Load('filename.txt', 'foo')

編集:

とにかく、__init__メソッドを省略したい場合は、__new__を試してください。

>>> class A(object):
...     def __init__(self):
...             print '__init__'
...
>>> A()
__init__
<__main__.A object at 0x800f1f710>
>>> a = A.__new__(A)
>>> a
<__main__.A object at 0x800f1fd50>
20

あなたの質問を文字通り受け止めて、メタクラスを使用します:

class MetaSkipInit(type):
    def __call__(cls):
        return cls.__new__(cls)


class B(object):
    __metaclass__ = MetaSkipInit

    def __init__(self):
        print "FAILURE"

    def Print(self):
        print "YEHAA"


b = B()
b.Print()

これは便利です。パラメーターリストを汚染せずにコンストラクターをコピーします。しかし、これを適切に行うには、提案されたハックよりも多くの作業と注意が必要です。

9
user8335

あんまり。 __init__の目的はオブジェクトをインスタンス化することであり、デフォルトでは実際には何もしません。 __init__メソッドが望んでいることをしておらず、変更する独自のコードではない場合、それを切り替えることもできます。たとえば、クラスAを使用して、その__init__メソッドの呼び出しを回避するために次のことができます。

def emptyinit(self):
    pass
A.__init__ = emptyinit
a = A()
a.Print()

これにより、クラスからどの__init__メソッドが動的に切り替えられ、空の呼び出しに置き換えられます。スーパークラスの__init__メソッドを呼び出さないため、これはおそらく良いことではないことに注意してください。

また、サブクラス化して、__init__メソッドをオーバーライドして目的の処理を行う(おそらく何もしない)ことを除いて、すべて同じ処理を行う独自のクラスを作成することもできます。

ただし、おそらく、オブジェクトをインスタンス化せずにクラスからメソッドを呼び出したいだけです。その場合は、 @ classmethod および @ staticmethod デコレーターを調べる必要があります。それらはまさにそのタイプの振る舞いを可能にします。

コードには、self引数を受け取らない@staticmethodデコレータを配置しました。おそらく、この目的に適しているのは@classmethodで、これは次のようになります。

@classmethod
def Load(cls, file, newName):
    # Get the data
    data = getdata()
    # Create an instance of B with the data
    return cls.B(newName, data)

更新:Roshの優秀な回答は、__init__を実装することで__new__の呼び出しを避けることができることを指摘しました。おかげでロッシュ!

8
Ryan Hiebert

私はPython cookbookを読んでいて、これについて話しているセクションがあります:例は__new__を使用して__init__()をバイパスします

 >>>クラスA:
 def __init __(self、a):
 self.a = a 
 
 
> >> test = A( 'a')
 >>> test.a 
 'a' 
 >>> test_noinit = A .__ new __(A)
 >>> test_noinit.a 
トレースバック(最新のコールラスト):
 File ""、1行目、
 test_noinit.a 
 AttributeError: 'A'オブジェクトには属性「a」がありません
 >>> 

ただし、これはPython3でのみ機能すると思います。以下は2.7で実行されています

 >>>クラスA:
 def __init __(self、a):
 self.a = a 
 
 
> >> test = A .__ new __(A)
 
トレースバック(最新のコールラスト):
ファイル ""、1行目
 test = A. __new __(A)
 AttributeError:クラスAには属性 '__new __' 
 >>> 
がありません
6
xbb

私のコメントで述べたように、__init__メソッドを使用して、パラメーターに値を指定せずに作成できるようにします。

def __init__(self, p0, p1, p2):
   # some logic

になるだろう:

def __init__(self, p0=None, p1=None, p2=None):
   if p0 and p1 and p2:
       # some logic

または:

def __init__(self, p0=None, p1=None, p2=None, init=True):
    if init:
        # some logic
2
pajton