Heim  >  Artikel  >  Backend-Entwicklung  >  Einführung in die neue Klassenmethode, die Init-Instanzmethode und den Singleton-Modus in Python (mit Beispielen)

Einführung in die neue Klassenmethode, die Init-Instanzmethode und den Singleton-Modus in Python (mit Beispielen)

不言
不言nach vorne
2019-01-28 11:18:552221Durchsuche

Dieser Artikel bietet Ihnen eine Einführung in die neue Klassenmethode, die Init-Instanzmethode und den Singleton-Modus (mit Beispielen). Ich hoffe, er wird Ihnen helfen.

„Sind alle Klassen im Python-Singleton-Modus?“ Eines Tages stellte mir ein Kollege eine solche Frage. Das ist eine seltsame Frage, vielleicht denken Sie das auch. Ohne hier eine Erklärung zu geben, werfen wir zunächst einen Blick auf die Methoden __new__ und __init__. Die Methoden

new und init

__new__ gehören zur New-Style-Klasse, also zur Objektklasse. Es ist eine statische Methode, aber ihr erster Parameter muss eine Klasse (cls) sein, was ein bisschen wie eine Klassenmethode ist. Tatsächlich kann sie als Klassenmethode betrachtet werden. Wenn diese spezielle Methode aufgerufen wird, wird eine neue Instanz der Klasse (cls) erstellt und zurückgegeben. Nachdem die Instanz erstellt wurde, übergibt der Interpreter die Instanz und andere Parameter an die Initialisierungsfunktion __init__ der Instanz, um die Instanz zu initialisieren.

Die Methode __new__ ist also eine Klassenmethode, die zum Erstellen einer Instanz verwendet wird, und die Methode __init__ ist eine Instanzmethode, die zum Initialisieren einer Instanz verwendet wird.

Die Methode __new__ wird beim Instanziieren einer Klasse aufgerufen. Das Überschreiben dieser Methode sollte wie folgt aussehen:

class A(object):

    def __new__(cls, *args, **kwargs)
        return super(A, cls).__new__(cls, *args, **kwargs)

Wenn die Methode __new__ keine Instanz von cls zurückgibt, wird die Methode __init__ der neuen Instanz zurückgegeben nicht aufgerufen werden. Es ist zu beachten, dass die neue Methode nach Python 3.3 keine zusätzlichen Parameter mehr erhält, andernfalls kommt es zu einer Ausnahme TypeError: object() Takes No Parameters.

Die Methode __init__ wird aufgerufen, nachdem die Instanz erstellt wurde. Diese Methode führt nur einige Initialisierungsvorgänge für die von der Methode __new__ erstellte Instanz aus. Beachten Sie, dass die Init-Methode immer aufgerufen wird, wenn die neue Methode eine Instanz zurückgibt (dies sollte besonders beachtet werden, wenn Sie die neue Methode zum Implementieren eines Singletons verwenden).

Sie können eine Überprüfung durchführen:

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

Ausgabeergebnis:

__new__ is called
__init__ is called
Foo(m=1, n=2)

Wir können also die Schlussfolgerung ziehen:

1. __new__ ist eine Methode auf Klassenebene, auch wenn sie nicht durch die Klassenmethode dekoriert wird der Generierung von Instanzen.

2. __init__ ist eine Methode auf Instanzebene, die den Instanzinitialisierungsprozess bestimmt, z. B. das Hinzufügen von Attributen, das Beurteilen und Konvertieren von Initialisierungsparametern usw.

Es ist zu beachten, dass die Parameter der neu geschriebenen __new__-Methode und der __init__-Methode konsistent sein sollten, da sonst TypeError auftritt. Wenn Sie object.__new__() direkt aufrufen, werden eingehende Parameter in Python 3.3 und späteren Versionen nicht mehr unterstützt. Diese Referenz stammt von: https://stackoverflow.com/questions/34777773/typeerror...

__init__ ist im Allgemeinen bei der Definition einer Klasse beteiligt und wird auch häufig verwendet. Die Methode __new__ wird selten verwendet. Wozu dient sie also?

Neue Methodenfunktion

Die am häufigsten verwendeten Funktionen der __new__-Methode sind wahrscheinlich:

1. Beim Erben integrierter unveränderlicher Typen (wie int, str, tuple) , stellen Sie selbst bereit. Definieren Sie den Instanziierungsprozess. Denn wenn Sie alle Schreibvorgänge in der Methode __init__ ausführen, ist diese möglicherweise ungültig. Zum Beispiel:

class CustomInt(int):

    def __init__(self, v):
        super(CustomInt, self).__init__(self, abs(v))

print CustomInt(-1)

# 输出:-1
Dies hat möglicherweise nicht den gewünschten Effekt. Dies kann jedoch durch Überschreiben der

-Methode erreicht werden: __new__

class CustomInt(int):

    def __new__(cls, v):
        return super(CustomInt, cls).__new__(cls, abs(v))

print CustomInt(-1)

# 输出:1
  • 2. Implementieren Sie eine benutzerdefinierte Metaklasse. Metaklasse wird verwendet, um zu definieren, wie eine Klasse erstellt wird. Das Konzept ist möglicherweise etwas kompliziert und wird hier nicht im Detail erläutert.

  • 3. Singleton implementieren. Da der Prozess der Generierung von Instanzen einer Klasse über die Methode

    gesteuert wird, ist es sehr praktisch, diese Methode neu zu schreiben, um einen Singleton-Modus zu erstellen: __new__

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()  # 断言成功
Der sogenannte Singleton Modus bedeutet, dass bei jeder Initialisierung dieselbe Instanz zurückgegeben wird, sodass die Speicheradresse des durch die beiden Initialisierungen erhaltenen Objekts dieselbe sein sollte:

print Singleton(), Singleton()
Das Ergebnis sollte offensichtlich sein:

<__main__.Singleton object at 0x10d698650> <__main__.Singleton object at 0x10d698650>
Der Dekorateur implementiert einen Singleton

Apropos Singleton-Modus: Neben der Verwendung der

-Methode gibt es auch andere Möglichkeiten, wie Dekoratoren, Metaklassen usw. Verschiedene Implementierungsarten haben unterschiedliche Auswirkungen. Metaklassen sind fortgeschrittenere Funktionen in Python und werden in diesem Artikel nicht besprochen. Schauen wir uns an, wie man Dekoratoren zum Implementieren von Singletons verwendet. __new__

Ein Dekorateur kann die Funktionalität einer Klasse oder Funktion dynamisch ändern, d. h. eine Klasse kann auch von einem Dekorateur dekoriert werden. Daher können Sie einen Dekorator verwenden, um eine Klasse so zu dekorieren, dass sie bei der Initialisierung nur eine Instanz generiert:

from functools import wraps

def singleton(cls):
    instances = {}
    @wraps(cls)
    def getinstance(*args, **kwargs):
        if cls not in instances:
            instances[cls] = cls(*args, **kwargs)
        return instances[cls]
    return getinstance

@singleton
class MyClass(object):

    def __init__(self):
        pass
Es ist zu beachten, dass

Wenn Sie einen Dekorator zum Implementieren eines Singletons verwenden, hat die Klasse werden zu Funktionen, nicht mehr zu Klassen. Beim Initialisieren der Instanz mit MyClass im obigen Beispiel wird tatsächlich die getinstance-Funktion aufgerufen, die nach der Dekoration zurückgegeben wird.

Der Unterschied zwischen der Verwendung von

zum Implementieren eines Singletons und der Verwendung von Dekoration zum Implementieren eines Singletons besteht darin, dass ersteres die __new__-Methode aufruft, was bedeutet, dass für jede Initialisierung unterschiedliche Parameter verwendet werden, obwohl die zurückgegebenen Parameter unterschiedlich sind Die Instanz ist dieselbe, die Eigenschaften der Instanz wurden jedoch zurückgesetzt. Letztere gibt immer die bei der ersten Initialisierung erstellte Instanz und die festgelegten Eigenschaften zurück, auch wenn später andere Parameter übergeben werden. __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 改为函数,提供了丰富的参数来控制输出的样式;对编码的调整等等。

Das obige ist der detaillierte Inhalt vonEinführung in die neue Klassenmethode, die Init-Instanzmethode und den Singleton-Modus in Python (mit Beispielen). Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Dieser Artikel ist reproduziert unter:segmentfault.com. Bei Verstößen wenden Sie sich bitte an admin@php.cn löschen