この記事では、Python の新しいクラス メソッド、init インスタンス メソッド、およびシングルトン モードを紹介します (例付き)。これには特定の参考値があります。必要な友人は参照できます。お役に立てば幸いです。
「すべてのクラスは Python シングルトン モードですか?」 ある日、同僚からこのような質問を受けました。奇妙な質問ですね、もしかしたらあなたもそう思っているかもしれません。ここでは説明は省略しますが、まず __new__ メソッドと __init__ メソッドを見てみましょう。
new メソッドと init
__new__ メソッドは、新しいスタイルのクラスに属します。つまり、オブジェクト クラスに属します。これは静的メソッドですが、最初のパラメータはクラス (cls) である必要があり、クラスメソッドに似ており、実際にはクラス メソッドとみなすこともできます。この特別なメソッドが呼び出されると、クラス (cls) の新しいインスタンスが作成されて返されます。インスタンスの作成後、インタプリタはインスタンスとその他のパラメータをインスタンスの初期化関数 __init__ に渡してインスタンスを初期化します。
つまり、__new__ メソッドはインスタンスの作成に使用されるクラス メソッドであり、__init__ メソッドはインスタンスの初期化に使用されるインスタンス メソッドです。
__new__ メソッドは、クラスをインスタンス化するときに呼び出されます。このメソッドをオーバーライドすると、次のようになります:
class A(object): def __new__(cls, *args, **kwargs) return super(A, cls).__new__(cls, *args, **kwargs)
__new__ メソッドが cls のインスタンスを返さない場合、新しいインスタンスの __init__ メソッド呼ばれることはありません。 Python 3.3 以降、新しいメソッドは追加のパラメーターを受け取らないことに注意してください。そうでない場合は、例外 TypeError: object() はパラメーターを受け取りません。
__init__ メソッドは、インスタンスの作成後に呼び出されます。このメソッドは、__new__ メソッドによって作成されたインスタンスに対して一部の初期化操作のみを実行します。新しいメソッドがインスタンスを返す場合、常に init メソッドが呼び出されることに注意してください (新しいメソッドを使用してシングルトンを実装する場合は、特に注意する必要があります)
いくつかの検証を行うことができます:
class Foo(object): def __new__(cls, m, n): print "__new__ is called" return super(Foo, cls).__new__(cls, m, n) def __init__(self, m, n): print "__init__ is called" self.m = m self.n = n def __repr__(self): return "Foo(m={self.m}, n={self.n})".format(self=self) def __str__(self): return self.__repr__() if __name__ == "__main__": f = Foo(1, 2) print f
出力結果:
__new__ is called __init__ is called Foo(m=1, n=2)
結論:
1. __new__ はクラスレベルのメソッドであり、クラスメソッドで修飾されていなくても処理を決定しますインスタンスの生成。
2. __init__ は、属性の追加、初期化パラメーターの判断と変換など、インスタンスの初期化プロセスを決定するインスタンス レベルのメソッドです。
書き換えた __new__ メソッドと __init__ メソッドのパラメータは一致している必要があります。一致していないと TypeError が発生します。 object.__new__() を直接呼び出すと、受信パラメータは Python 3.3 以降のバージョンでサポートされなくなります。 #__init__ メソッドは通常、クラスを定義するときに関与し、よく使用されます。 __new__ メソッドはほとんど使用されませんが、どのような用途があるのでしょうか?
新しいメソッド関数
1. 組み込みの不変型 (int、str、など) を継承する場合。タプル)、インスタンス化プロセスを自己定義します。 __init__ メソッドですべての書き込み操作を実行すると、無効になる可能性があるためです。例:
class CustomInt(int): def __init__(self, v): super(CustomInt, self).__init__(self, abs(v)) print CustomInt(-1) # 输出:-1
これでは、期待どおりの効果が得られない可能性があります。ただし、
__new__ メソッドをオーバーライドすることで実現できます: <pre class="brush:php;toolbar:false">class CustomInt(int):
def __new__(cls, v):
return super(CustomInt, cls).__new__(cls, abs(v))
print CustomInt(-1)
# 输出:1</pre>
メソッドによって制御されるため、シングルトン モードを使用するようにこのメソッドを書き直すと非常に便利です。いわゆるシングルトン このモードは、初期化されるたびに同じインスタンスを返すことになっているため、2 回の初期化によって取得されるオブジェクトのメモリ アドレスは同じである必要があります: <pre class="brush:php;toolbar:false">class Singleton(object):
def __new__(cls):
if not hasattr(cls, "_instance"):
cls._instance = super(Singleton, cls).__new__(cls)
return cls._instance
assert Singleton() is Singleton() # 断言成功</pre>
結果は明らかです:
print Singleton(), Singleton()
シングルトン モードと言えば、
__new__メソッドを使用する以外にも、デコレータやメタクラスなど、いくつかの方法があります。実装方法が異なれば効果も異なります。メタクラスは Python のより高度な機能であるため、この記事では説明しません。デコレータを使用してシングルトンを実装する方法を見てみましょう。
<__main__.Singleton object at 0x10d698650> <__main__.Singleton object at 0x10d698650>
デコレータを使用してシングルトンを実装する場合、クラスはもうクラスではなく関数になります。
上記の例で MyClass を使用してインスタンスを初期化する場合、実際に呼び出されるのは、デコレートされた後に返される getinstance 関数です。
を使用してシングルトンを実装する場合と、デコレーションを使用してシングルトンを実装する場合の違いは、前者は __init__ メソッドを呼び出すことです。つまり、毎回異なるパラメータで初期化されます。返されるインスタンスは同じですが、インスタンスのプロパティはリセットされています。後者は、後で異なるパラメータが渡された場合でも、最初の初期化中に作成されたサンプルと設定されたプロパティを常に返します。
接着,我们再来看一个 “奇怪” 的现象:
>>> class A(object): ... pass ... >>> print A(), A() <__main__.A object at 0x104765450> <__main__.A object at 0x104765450> >>> print A(), A() <__main__.A object at 0x104765450> <__main__.A object at 0x104765450> >>> print A(), A() <__main__.A object at 0x104765450> <__main__.A object at 0x104765450>
是不是感觉有些难以置信,print 语句后两次创建的对象应该是不一样的,而他们却莫名奇妙的一样。这就是我讨论本文内容的原因。
一次同事问我,Python 中的类都是单例模式?我当时一脸懵逼,听了他的描述,我自己也试了下,果然存在如上所示的“奇怪”现象。于是我就去了解了 Python 单例模式的实现,在了解到 __new__
的实现方式时,就想对 __new__
和 __init__
有一个更加深入的了解。于是就有了本文所讨论的内容。
然后,我想着用 is 来判断下他们是否真的是同一个实例:
>>> A() is A() False
我没有对 CPython 的源码进行过全面的阅读,所以对其很多内部的实现机制不是太了解。我猜 Python 解释器在内部可能做了优化,像 print A(), A()
这样的语句,解释器认为没有必要创建不同的对象,直接返回同一个实例的引用得了。是不是觉得解释器有些自作聪明!而当 A() is A()
这样的表达式出现时,解释器想,我不能再自作聪明,那样可能会误导别人。可是,在 print 语句那样的用法时,就已经误导我了,我都差点开始怀疑人生了!
从语法来看,大家应该知道,我在测试时使用的 Python 2。我后来也试了下 Python 3:
>>> class A(): ... pass ... >>> print(A(), A()) <__console__.A object at 0x10fe7afd0> <__console__.A object at 0x10fed79e8> >>> print(A(), A()) <__console__.A object at 0x10fec0cc0> <__console__.A object at 0x10feda160> >>> print(A(), A()) <__console__.A object at 0x10fe7afd0> <__console__.A object at 0x10fed7940> >>> A() is A() False
我想,这样的结果才是不会让人困惑的。可能是 Python 社区意识到了这个问题并在 Python3 中进行了修正。这样的修正是好的,否则对于像我同事那样初次使用 Python 的人来说是很困惑的。
个人认为,Python3 对过去的一些“错误”的修正是好的。例如将 print 改为函数,提供了丰富的参数来控制输出的样式;对编码的调整等等。
以上がPython の新しいクラス メソッド、init インスタンス メソッド、シングルトン モードの紹介 (例付き)の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。