Cet article vous présente une introduction à la nouvelle méthode de classe, à la méthode d'instance init et au mode singleton en Python (avec des exemples). Il a une certaine valeur de référence. Les amis dans le besoin peuvent s'y référer.
"Est-ce que toutes les classes sont en mode Python singleton ?" Un jour, un collègue m'a posé une telle question. C'est une question étrange, peut-être que vous le pensez aussi. Sans explication ici, examinons d'abord les méthodes __new__ et __init__. Les méthodes
new et init
__new__ appartiennent à la classe new-style, c'est-à-dire qu'elles appartiennent à la classe objet. C'est une méthode statique, mais son premier paramètre doit être une classe (cls), ce qui ressemble un peu à une méthode de classe. En fait, elle peut être considérée comme une méthode de classe. Lorsque cette méthode spéciale est appelée, une nouvelle instance de la classe (cls) sera créée et renvoyée. Une fois l'instance créée, l'interpréteur transmettra l'instance et les autres paramètres à la fonction d'initialisation de l'instance __init__ pour initialiser l'instance.
Ainsi, la méthode __new__ est une méthode de classe, utilisée pour créer une instance, et la méthode __init__ est une méthode d'instance, utilisée pour initialiser une instance.
La méthode __new__ est appelée lors de l'instanciation d'une classe. Le remplacement de cette méthode devrait ressembler à ce qui suit :
class A(object): def __new__(cls, *args, **kwargs) return super(A, cls).__new__(cls, *args, **kwargs)
Si la méthode __new__ ne renvoie pas d'instance de cls, alors la nouvelle instance __init__. la méthode ne sera pas appelée. Il est à noter qu'après Python 3.3, la nouvelle méthode ne reçoit plus de paramètres supplémentaires, sinon il y aura une exception TypeError : object() ne prend aucun paramètre.
La méthode __init__ est appelée après la création de l'instance. Cette méthode effectue uniquement certaines opérations d'initialisation sur l'instance créée par la méthode __new__. Notez que si la nouvelle méthode renvoie une instance, la méthode init sera toujours appelée (cela doit faire l'objet d'une attention particulière lors de l'utilisation de la nouvelle méthode pour implémenter un singleton)
Vous pouvez faire une vérification :
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
Résultat de sortie :
__new__ is called __init__ is called Foo(m=1, n=2)
Nous pouvons donc tirer la conclusion :
1 __new__ est une méthode au niveau de la classe, même si elle n'est pas décorée par classmethod, il détermine le processus de génération des instances.
2. __init__ est une méthode au niveau de l'instance qui détermine le processus d'initialisation de l'instance, comme l'ajout d'attributs, le jugement et la conversion des paramètres d'initialisation, etc.
Il convient de noter que les paramètres de la méthode __new__ réécrite et de la méthode __init__ doivent être cohérents, sinon TypeError se produira. Si vous appelez directement object.__new__(), les paramètres entrants ne sont plus pris en charge dans Python 3.3 et les versions ultérieures. Cette référence provient de : https://stackoverflow.com/questions/34777773/typeerror...
__init__ est généralement impliquée dans la définition d’une classe et est également couramment utilisée. La méthode __new__ est rarement utilisée, alors à quoi sert-elle ? Nouvelle fonction de méthodeLes fonctions les plus couramment utilisées de la méthode __new__ sont probablement : 1 Lors de l'héritage de types immuables intégrés (tels que int, str, tuple) , fournissez l'auto- Définir le processus d'instanciation. Parce que si vous effectuez toutes les opérations d'écriture dans la méthode __init__, elle peut être invalide. Par exemple :class CustomInt(int): def __init__(self, v): super(CustomInt, self).__init__(self, abs(v)) print CustomInt(-1) # 输出:-1
: __new__
class CustomInt(int): def __new__(cls, v): return super(CustomInt, cls).__new__(cls, abs(v)) print CustomInt(-1) # 输出:1
, il est très pratique de réécrire cette méthode pour créer un mode singleton : __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() # 断言成功
print Singleton(), Singleton()
<__main__.Singleton object at 0x10d698650> <__main__.Singleton object at 0x10d698650>
, il existe d'autres moyens, tels que les décorateurs, les métaclasses, etc. Différentes méthodes d'implémentation ont des effets différents. Les métaclasses sont des fonctionnalités plus avancées de Python et ne seront pas abordées dans cet article. Voyons comment utiliser les décorateurs pour implémenter des singletons. __new__
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
Lors de l'utilisation d'un décorateur pour implémenter un singleton, le la classe est devenue une fonction au lieu d'une classe. Lors de l'initialisation de l'instance à l'aide de MyClass dans l'exemple ci-dessus, ce qu'on appelle en réalité est la fonction getinstance renvoyée après avoir été décorée.
La différence entre utiliser pour implémenter un singleton et utiliser decoration pour implémenter un singleton est que le premier appellera la méthode __new__
, ce qui signifie que différents paramètres sont utilisés pour chaque initialisation, bien que le retour L'instance est la même, mais les propriétés de l'instance ont été réinitialisées ; et cette dernière renvoie toujours l'instance créée et les propriétés définies lors de la première initialisation, même si des paramètres différents sont passés ultérieurement. __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 改为函数,提供了丰富的参数来控制输出的样式;对编码的调整等等。
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!