私は自分のクラスの1つを合理化しようとしているだけで、 flyweightデザインパターン と同じスタイルでいくつかの機能を導入しました。
しかし、なぜ__init__
が常に__new__
の後に呼び出されるのかについては少し混乱しています。私はこれを期待していませんでした。なぜこれが起こっているのか、そうでなければどうやってこの機能を実装できるのか、誰かに教えてもらえますか? (実装を__new__
に入れることは別ですが、これはかなり厄介です。)
これが例です:
class A(object):
_dict = dict()
def __new__(cls):
if 'key' in A._dict:
print "EXISTS"
return A._dict['key']
else:
print "NEW"
return super(A, cls).__new__(cls)
def __init__(self):
print "INIT"
A._dict['key'] = self
print ""
a1 = A()
a2 = A()
a3 = A()
出力:
NEW
INIT
EXISTS
INIT
EXISTS
INIT
どうして?
新しいインスタンスの作成を制御する必要がある場合は、_ NEW _を使用してください。新しいインスタンスの初期化を制御する必要がある場合は、_ INIT _を使用してください。
_ NEW _は、インスタンス作成の最初のステップです。最初に呼び出され、クラスの新しいインスタンスを返す責任があります。対照的に、_ INIT _は何も返しません。インスタンスが作成された後に初期化するのは、それだけです。
一般的に、str、int、unicode、Tupleなどの不変の型をサブクラス化している場合を除き、_ NEW _をオーバーライドする必要はありません。
投稿者: http://mail.python.org/pipermail/tutor/2008-April/061426.html
あなたがやろうとしていることは通常 Factory で行われることを考慮する必要があります、そしてそれがそれをするための最良の方法です。 _ NEW _を使用するのは良い解決策ではないので、ファクトリーの使用法を検討してください。ここにあなたは 良い工場例 を持っています。
__new__
は静的クラスメソッドですが、__init__
はインスタンスメソッドです。 __new__
は最初にインスタンスを作成しなければならないので、__init__
はそれを初期化できます。 __init__
はパラメータとしてself
を取ります。インスタンスを作成するまで、self
はありません。
さて、私はあなたがPythonで シングルトンパターン を実装しようとしていると集める。それにはいくつかの方法があります。
また、Python 2.6以降、class デコレータ を使うことができます。
def singleton(cls):
instances = {}
def getinstance():
if cls not in instances:
instances[cls] = cls()
return instances[cls]
return getinstance
@singleton
class MyClass:
...
最もよく知られているOO言語では、SomeClass(arg1, arg2)
のような式は新しいインスタンスを割り当て、インスタンスの属性を初期化してから返します。
最もよく知られているOO言語では、constructorを定義することにより、クラスごとに「インスタンスの属性を初期化する」部分をカスタマイズできます。 (コンストラクター式に提供された引数を使用して)新しいインスタンスで、必要な初期条件を設定します。 Pythonでは、これはクラスの__init__
メソッドに対応します。
Pythonの__new__
は、「新しいインスタンスを割り当てる」部分のクラスごとの同様のカスタマイズにほかなりません。もちろん、これにより、新しいインスタンスを割り当てるのではなく、既存のインスタンスを返すなどの異常なことを行うことができます。そのため、Pythonでは、この部分を必ずしも割り当てに関係していると考えるべきではありません。必要なのは、__new__
がどこかから適切なインスタンスを作成することです。
しかし、それはまだ仕事の半分に過ぎず、Pythonシステムが、後で仕事の残りの半分(__init__
)を実行したい場合と、そうしない場合があることを知る方法がありません。 t。その動作が必要な場合は、そのように明示的に言わなければなりません。
多くの場合、__new__
のみを必要とするように、または__new__
を必要としないように、または__init__
が既に初期化されたオブジェクトに対して異なる動作をするようにリファクタリングできます。ただし、本当に必要な場合は、Pythonを使用して「ジョブ」を再定義できるため、SomeClass(arg1, arg2)
が__new__
に続いて__init__
を呼び出すとは限りません。これを行うには、メタクラスを作成し、__call__
メソッドを定義する必要があります。
メタクラスはクラスのクラスです。また、クラスの__call__
メソッドは、クラスのインスタンスを呼び出したときに何が起こるかを制御します。そのため、metaclass '__call__
メソッドは、クラスを呼び出したときに何が起こるかを制御します。つまり、インスタンス作成メカニズムを最初から最後まで再定義するができます。これは、シングルトンパターンなどの完全に非標準のインスタンス作成プロセスを最もエレガントに実装できるレベルです。実際、10行未満のコードでSingleton
メタクラスを実装できます。このメタクラスは、__new__
at)を使用する必要さえなく、anyそれ以外の場合は、__metaclass__ = Singleton
!を追加するだけで、シングルトンに通常のクラスが追加されます。
class Singleton(type):
def __init__(self, *args, **kwargs):
super(Singleton, self).__init__(*args, **kwargs)
self.__instance = None
def __call__(self, *args, **kwargs):
if self.__instance is None:
self.__instance = super(Singleton, self).__call__(*args, **kwargs)
return self.__instance
ただし、これはおそらく、この状況で実際に保証されるよりも深い魔法です!
典型的な実装は、適切な引数で "super(currentclass、cls).__ new __(cls [、...])"を使用してスーパークラスの__new __()メソッドを呼び出し、必要に応じて新しく作成されたインスタンスを変更することでクラスの新しいインスタンスを作成します。それを返す前に。
...
__new __()がclsのインスタンスを返さない場合、新しいインスタンスの__init __()メソッドは呼び出されません。
__new __()は、主に不変型(int、str、Tupleなど)のサブクラスでインスタンス作成をカスタマイズできるようにすることを目的としています。
私はこの質問はかなり古いことを認識していますが、私は同様の問題を抱えていました。以下は私が欲しかったことをしました:
class Agent(object):
_agents = dict()
def __new__(cls, *p):
number = p[0]
if not number in cls._agents:
cls._agents[number] = object.__new__(cls)
return cls._agents[number]
def __init__(self, number):
self.number = number
def __eq__(self, rhs):
return self.number == rhs.number
Agent("a") is Agent("a") == True
このページをリソースとして使用しました http://infohost.nmt.edu/tcc/help/pubs/python/web/new-new-method.html
__new__
が同じクラスのインスタンスを返すとき、__init__
は後で返されたオブジェクトに対して実行されます。すなわち__new__
が実行されないようにするために__init__
を使用することはできません。 __new__
から以前に作成したオブジェクトを返したとしても、__init__
によって初期化されたdouble(tripleなど)になります。
これは上記のvartecの答えを拡張してそれを修正するシングルトンパターンへの一般的なアプローチです:
def SingletonClass(cls):
class Single(cls):
__doc__ = cls.__doc__
_initialized = False
_instance = None
def __new__(cls, *args, **kwargs):
if not cls._instance:
cls._instance = super(Single, cls).__new__(cls, *args, **kwargs)
return cls._instance
def __init__(self, *args, **kwargs):
if self._initialized:
return
super(Single, self).__init__(*args, **kwargs)
self.__class__._initialized = True # Its crucial to set this variable on the class!
return Single
全文は ここ です。
実際に__new__
を含む別のアプローチはclassmethodsを使うことです:
class Singleton(object):
__initialized = False
def __new__(cls, *args, **kwargs):
if not cls.__initialized:
cls.__init__(*args, **kwargs)
cls.__initialized = True
return cls
class MyClass(Singleton):
@classmethod
def __init__(cls, x, y):
print "init is here"
@classmethod
def do(cls):
print "doing stuff"
このアプローチでは、すべてのメソッドを@classmethod
で装飾する必要があることに注意してください。実際のMyClass
のインスタンスを使用することは決してないでしょう。
この質問に対する簡単な答えは、__new__
がクラスと同じ型の値を返す場合、__init__
関数が実行されるが、それ以外の場合は実行されないことです。この場合、コードはA._dict('key')
を返します。これはcls
と同じクラスです。そのため、__init__
が実行されます。
class M(type):
_dict = {}
def __call__(cls, key):
if key in cls._dict:
print 'EXISTS'
return cls._dict[key]
else:
print 'NEW'
instance = super(M, cls).__call__(key)
cls._dict[key] = instance
return instance
class A(object):
__metaclass__ = M
def __init__(self, key):
print 'INIT'
self.key = key
print
a1 = A('aaa')
a2 = A('bbb')
a3 = A('aaa')
出力:
NEW
INIT
NEW
INIT
EXISTS
注意副作用としてM._dict
プロパティは自動的にA
からA._dict
としてアクセス可能になるので、誤って上書きしないように注意してください。
__new__は、クラスの新しい空白のインスタンスを返します。そのインスタンスを初期化するために__init__が呼び出されます。 __new__の "NEW"の場合は__init__を呼び出していないので、それが呼び出されます。 __new__
を呼び出しているコードは、__ init__が特定のインスタンスに対して呼び出されたかどうかを追跡しません。
__init__関数でオブジェクトに属性を追加して、それが初期化されたことを示すことができます。その属性が__init__の最初のものとして存在しているかどうかを確認してください。
@AntonyHatchkinsの回答を更新しました。おそらくメタタイプのクラスごとに個別のインスタンスのディクショナリが必要です。つまり、クラス全体をグローバルにするのではなく、そのディクショナリでクラスオブジェクトを初期化する__init__
メソッドが必要です。
class MetaQuasiSingleton(type):
def __init__(cls, name, bases, attibutes):
cls._dict = {}
def __call__(cls, key):
if key in cls._dict:
print('EXISTS')
instance = cls._dict[key]
else:
print('NEW')
instance = super().__call__(key)
cls._dict[key] = instance
return instance
class A(metaclass=MetaQuasiSingleton):
def __init__(self, key):
print 'INIT'
self.key = key
print()
私は先に進んで__init__
メソッドを使って元のコードを更新し、構文をPython 3の表記法に変更しました(属性としてではなくクラス引数で引数なしのsuper
とメタクラスを呼び出す)。
どちらにしても、ここで重要な点は、キーが見つかった場合にクラス初期化子(__call__
メソッド)が__new__
または__init__
を実行しないことです。これは、デフォルトの__new__
ステップをスキップしたい場合はオブジェクトをマークする必要がある__init__
を使用するよりもはるかにクリーンです。
伝統的なOO言語では、__init__
を単純なコンストラクタとして見てください。たとえば、JavaまたはC++に精通している場合は、コンストラクタにはそれ自体のインスタンスへのポインタが暗黙的に渡されます。 Javaの場合、それはthis
変数です。 Java用に生成されたバイトコードを検査すると、2回の呼び出しに気付くでしょう。最初の呼び出しは "new"メソッドへの呼び出しであり、次に次の呼び出しはinitメソッドへの呼び出しです(これはユーザー定義コンストラクターへの実際の呼び出しです)。この2段階のプロセスにより、そのインスタンスの別のメソッドであるクラスのコンストラクターメソッドを呼び出す前に、実際のインスタンスを作成できます。
現在、Pythonの場合、__new__
はユーザーがアクセスできる追加機能です。型付きの性質上、Javaはその柔軟性を提供しません。言語がその機能を提供している場合、__new__
の実装者は、インスタンスを返す前に、そのメソッドで多くのことを行うことができます。そしてこのアプローチは、特にPythonの場合の不変型に対してもうまく機能します。
数字や文字列のような不変の組み込み型をサブクラス化するとき、そして時折他の状況では、静的メソッド new が役に立ちます。 new はインスタンス構築の最初のステップで、 init の前に呼び出されます。
new メソッドはクラスを最初の引数として呼び出されます。その責任はそのクラスの新しいインスタンスを返すことです。
これを init と比較してください。 init はインスタンスを最初の引数として呼び出され、何も返しません。その責任はインスタンスを初期化することです。
init を呼び出さずに新しいインスタンスが作成される場合があります(たとえば、インスタンスがpickleからロードされたときなど)。 new を呼び出さずに新しいインスタンスを作成する方法はありません(ただし、場合によっては基本クラスの new を呼び出すことで回避できます)。 。
あなたが達成したいことに関しては、シングルトンパターンについての同じdoc情報にもあります
class Singleton(object):
def __new__(cls, *args, **kwds):
it = cls.__dict__.get("__it__")
if it is not None:
return it
cls.__it__ = it = object.__new__(cls)
it.init(*args, **kwds)
return it
def init(self, *args, **kwds):
pass
デコレータを使って、PEP 318からこの実装を使うこともできます。
def singleton(cls):
instances = {}
def getinstance():
if cls not in instances:
instances[cls] = cls()
return instances[cls]
return getinstance
@singleton
class MyClass:
...
もう少し掘り下げてみましょう!
CPythonのジェネリッククラスの型はtype
であり、その基本クラスはObject
です(メタクラスなどの別の基本クラスを明示的に定義しない限り)。低レベルの呼び出しのシーケンスは here にあります。最初に呼び出されるメソッドはtype_call
で、次にtp_new
を呼び出し、次にtp_init
を呼び出します。
ここで興味深いのは、tp_new
がObject
の(基本クラス)新しいメソッドobject_new
を呼び出して、メモリを割り当てるtp_alloc
(PyType_GenericAlloc
)を呼び出すことです。オブジェクト :)
その時点で、オブジェクトがメモリに作成され、__init__
メソッドが呼び出されます。 __init__
がクラスに実装されていない場合、object_init
が呼び出され、何もしません:)
type_call
は、変数にバインドするオブジェクトを返すだけです。
__init__
は__new__
の後に呼び出されるため、サブクラスでオーバーライドしても、追加されたコードは呼び出されます。
すでに__new__
を持つクラスをサブクラス化しようとしている場合、これに気付いていない誰かが__init__
を適応させてサブクラス__init__
に呼び出しを転送することから始めるかもしれません。 __init__
の後に__new__
を呼び出すというこの規則は、それが期待通りに機能するのを助けます。
__init__
は、スーパークラス__new__
が必要とするすべてのパラメータを依然として許可する必要がありますが、そうしないと、通常明確な実行時エラーが発生します。そして__new__
はおそらく明示的に*args
と '** kw'を許すべきです。
元の投稿者が述べた振る舞いのため、__new__
と__init__
の両方を同じクラスの継承で同じクラスに持つのは一般的に悪い形式です。
しかし、なぜ
__init__
が常に__new__
の後に呼び出されるのかについては少し混乱しています。
ここではC++の類推が役に立つと思います。
__new__
は単にオブジェクトにメモリを割り当てます。オブジェクトのインスタンス変数はそれを保持するためにメモリを必要とします、そしてこれは__new__
ステップがすることです。
__init__
はオブジェクトの内部変数を特定の値に初期化します(デフォルトでも構いません)。
しかし、なぜ
__init__
が常に__new__
の後に呼び出されるのかについては少し混乱しています。
それが単にそのように行われるということ以外の理由はそれほど多くはありません。 __new__
にはクラスを初期化する責任はありませんが、他の方法にはあります(__call__
、おそらく - 私はよくわかりません)。
私はこれを期待していませんでした。なぜこれが起こっているのか、そうでなければどうやってこの機能を実装するのか、誰かに教えてもらえますか? (実装を
__new__
に入れることは別として、それはかなりハックな感じがします)。
すでに初期化されている場合は__init__
に何もしないようにするか、新しいインスタンスで__call__
を呼び出すだけの新しい__init__
を使用して新しいメタクラスを作成し、それ以外の場合は__new__(...)
を返すことができます。
今、私は同じ問題を抱えています、そしていくつかの理由で私はデコレータ、工場とメタクラスを避けることに決めました。私はこのようにしました:
def _alt(func):
import functools
@functools.wraps(func)
def init(self, *p, **k):
if hasattr(self, "parent_initialized"):
return
else:
self.parent_initialized = True
func(self, *p, **k)
return init
class Parent:
# Empty dictionary, shouldn't ever be filled with anything else
parent_cache = {}
def __new__(cls, n, *args, **kwargs):
# Checks if object with this ID (n) has been created
if n in cls.parent_cache:
# It was, return it
return cls.parent_cache[n]
else:
# Check if it was modified by this function
if not hasattr(cls, "parent_modified"):
# Add the attribute
cls.parent_modified = True
cls.parent_cache = {}
# Apply it
cls.__init__ = _alt(cls.__init__)
# Get the instance
obj = super().__new__(cls)
# Push it to cache
cls.parent_cache[n] = obj
# Return it
return obj
class A(Parent):
def __init__(self, n):
print("A.__init__", n)
class B(Parent):
def __init__(self, n):
print("B.__init__", n)
>>> A(1)
A.__init__ 1 # First A(1) initialized
<__main__.A object at 0x000001A73A4A2E48>
>>> A(1) # Returned previous A(1)
<__main__.A object at 0x000001A73A4A2E48>
>>> A(2)
A.__init__ 2 # First A(2) initialized
<__main__.A object at 0x000001A7395D9C88>
>>> B(2)
B.__init__ 2 # B class doesn't collide with A, thanks to separate cache
<__main__.B object at 0x000001A73951B080>
単純な理由は、インスタンスの作成に new が使用され、インスタンスの初期化に init が使用されるためです。初期化する前に、インスタンスを最初に作成する必要があります。だからこそ new は init の前に呼ばれるべきです。