Quelle est la différence entre les classes de style ancien et nouveau en Python? Quand dois-je utiliser l'un ou l'autre?
Quelle est la différence entre les classes de style ancien et nouveau en Python? Quand dois-je utiliser l'un ou l'autre?
Réponses:
Des classes New-style et classiques :
Jusqu'à Python 2.1, les classes à l'ancienne étaient la seule version disponible pour l'utilisateur.
Le concept de classe (à l'ancienne) n'est pas lié au concept de type: si
x
est une instance d'une classe à l'ancienne, ilx.__class__
désigne alors la classe dex
, mais l'type(x)
est toujours<type 'instance'>
.Cela reflète le fait que toutes les instances de style ancien, indépendamment de leur classe, sont implémentées avec un seul type intégré, appelé instance.
Des classes de nouveau style ont été introduites dans Python 2.2 pour unifier les concepts de classe et de type . Une classe de nouveau style est simplement un type défini par l'utilisateur, ni plus, ni moins.
Si x est une instance d'une classe de nouveau style, alors
type(x)
est généralement le même quex.__class__
(bien que cela ne soit pas garanti - une instance de classe de nouveau style est autorisée à remplacer la valeur renvoyée pourx.__class__
).La principale motivation pour introduire des classes de nouveau style est de fournir un modèle objet unifié avec un méta-modèle complet .
Il présente également un certain nombre d'avantages immédiats, comme la possibilité de sous-classer la plupart des types intégrés ou l'introduction de «descripteurs», qui permettent des propriétés calculées.
Pour des raisons de compatibilité, les classes sont toujours à l'ancienne par défaut .
Les classes de nouveau style sont créées en spécifiant une autre classe de nouveau style (c'est-à-dire un type) comme classe parente, ou l'objet "type de niveau supérieur" si aucun autre parent n'est nécessaire.
Le comportement des classes de style nouveau diffère de celui des classes de style ancien par un certain nombre de détails importants en plus du type renvoyé.
Certaines de ces modifications sont fondamentales pour le nouveau modèle d'objet, comme la façon dont les méthodes spéciales sont appelées. D'autres sont des "correctifs" qui ne pouvaient pas être implémentés auparavant pour des raisons de compatibilité, comme l'ordre de résolution des méthodes en cas d'héritage multiple.
Python 3 n'a que des classes de nouveau style .
Peu importe si vous sous-classe
object
ou non, les classes sont de nouveau style en Python 3.
super()
ne fonctionnent pas sur les classes à l'ancienne. Sans oublier, comme le dit cet article, qu'il existe des correctifs fondamentaux, comme MRO, et des méthodes spéciales, ce qui est plus qu'une bonne raison de l'utiliser.
Côté déclaration:
Les classes de nouveau style héritent de l' objet ou d'une autre classe de nouveau style.
class NewStyleClass(object):
pass
class AnotherNewStyleClass(NewStyleClass):
pass
Les classes à l'ancienne ne le font pas.
class OldStyleClass():
pass
Python 3 Remarque:
Python 3 ne prend pas en charge les anciennes classes de style, donc l'une ou l'autre forme notée ci-dessus donne une classe de nouveau style.
object
.
class AnotherOldStyleClass: pass
class A: pass
et class A(): pass
sont strictement équivalents. Le premier signifie "A n'hérite d'aucune classe parent" et le second signifie "A n'hérite d'aucune classe parent" . C'est assez similaire à not is
etis not
Changements de comportement importants entre les anciennes et les nouvelles classes de style
Exception
(exemple ci-dessous)__slots__
ajoutéeIl a été mentionné dans d'autres réponses, mais voici un exemple concret de la différence entre le MRO classique et le C3 MRO (utilisé dans les nouvelles classes de style).
La question est l'ordre dans lequel les attributs (qui incluent les méthodes et les variables membres) sont recherchés dans l'héritage multiple.
Les classes classiques effectuent une recherche approfondie de gauche à droite. Arrêtez-vous au premier match. Ils n'ont pas d' __mro__
attribut.
class C: i = 0
class C1(C): pass
class C2(C): i = 2
class C12(C1, C2): pass
class C21(C2, C1): pass
assert C12().i == 0
assert C21().i == 2
try:
C12.__mro__
except AttributeError:
pass
else:
assert False
Les classes de nouveau style MRO sont plus compliquées à synthétiser en une seule phrase anglaise. Il est expliqué en détail ici . L'une de ses propriétés est qu'une classe de base n'est recherchée qu'une fois que toutes ses classes dérivées l'ont été. Ils ont l' __mro__
attribut qui montre l'ordre de recherche.
class C(object): i = 0
class C1(C): pass
class C2(C): i = 2
class C12(C1, C2): pass
class C21(C2, C1): pass
assert C12().i == 2
assert C21().i == 2
assert C12.__mro__ == (C12, C1, C2, C, object)
assert C21.__mro__ == (C21, C2, C1, C, object)
Exception
Autour de Python 2.5, de nombreuses classes pouvaient être augmentées, et autour de Python 2.6, cela a été supprimé. Sur Python 2.7.3:
# OK, old:
class Old: pass
try:
raise Old()
except Old:
pass
else:
assert False
# TypeError, new not derived from `Exception`.
class New(object): pass
try:
raise New()
except TypeError:
pass
else:
assert False
# OK, derived from `Exception`.
class New(Exception): pass
try:
raise New()
except New:
pass
else:
assert False
# `'str'` is a new style object, so you can't raise it:
try:
raise 'str'
except TypeError:
pass
else:
assert False
Les anciennes classes de style sont encore légèrement plus rapides pour la recherche d'attributs. Ce n'est généralement pas important, mais il peut être utile dans le code Python 2.x sensible aux performances:
Dans [3]: classe A: ...: def __init __ (auto): ...: self.a = 'salut là' ...: Dans [4]: classe B (objet): ...: def __init __ (auto): ...: self.a = 'salut là' ...: Dans [6]: aobj = A () Dans [7]: bobj = B () Dans [8]:% timeit aobj.a 10000000 boucles, meilleur de 3: 78,7 ns par boucle Dans [10]:% timeit bobj.a 10000000 boucles, meilleur de 3: 86,9 ns par boucle
%timeit aobj.a
10000000 loops, best of 3: 66.1 ns per loop
%timeit bobj.a
10000000 loops, best of 3: 53.9 ns per loop
Guido a écrit The Inside Story on New-Style Classes , un très bon article sur les classes de style nouveau et ancien en Python.
Python 3 n'a qu'une nouvelle classe de style. Même si vous écrivez une «classe à l'ancienne», elle est implicitement dérivée de object
.
Les classes de nouveau style ont des fonctionnalités avancées qui font défaut aux classes de style ancien, telles que super
la nouvelle C3 mro , certaines méthodes magiques, etc.
Voici une différence très pratique, vraie / fausse. La seule différence entre les deux versions du code suivant est que dans la deuxième version, Person hérite de object . En dehors de cela, les deux versions sont identiques, mais avec des résultats différents:
Cours à l'ancienne
class Person():
_names_cache = {}
def __init__(self,name):
self.name = name
def __new__(cls,name):
return cls._names_cache.setdefault(name,object.__new__(cls,name))
ahmed1 = Person("Ahmed")
ahmed2 = Person("Ahmed")
print ahmed1 is ahmed2
print ahmed1
print ahmed2
>>> False
<__main__.Person instance at 0xb74acf8c>
<__main__.Person instance at 0xb74ac6cc>
>>>
Classes de nouveau style
class Person(object):
_names_cache = {}
def __init__(self,name):
self.name = name
def __new__(cls,name):
return cls._names_cache.setdefault(name,object.__new__(cls,name))
ahmed1 = Person("Ahmed")
ahmed2 = Person("Ahmed")
print ahmed2 is ahmed1
print ahmed1
print ahmed2
>>> True
<__main__.Person object at 0xb74ac66c>
<__main__.Person object at 0xb74ac66c>
>>>
_names_cache
est un dictionnaire qui met en cache (stocke pour une récupération future) chaque nom que vous passez Person.__new__
. La méthode setdefault (définie dans n'importe quel dictionnaire) prend deux arguments: une clé et une valeur. Si la clé est dans le dict, elle renverra sa valeur. Si ce n'est pas dans le dict, il le mettra d'abord à la valeur passée comme deuxième argument, puis le renverra.
__new__()
est toujours appelé, et il construit toujours un nouvel objet, puis le lance. Dans ce cas, a if
est préférable à .setdefault()
.
__new__
n'est pas vraiment une chose pour les classes à l'ancienne, il n'est pas utilisé dans la construction d'instance (c'est juste un nom aléatoire qui a l'air spécial, comme définir __spam__
). Ainsi, la construction de la classe à l'ancienne ne fait qu'appeler __init__
, tandis que la construction à nouveau style invoque __new__
(fusionnant à une instance singleton par son nom) pour la construire et l' __init__
initialiser.
Les classes de nouveau style héritent de object
et doivent être écrites comme telles dans Python 2.2 (c'est- class Classname(object):
à- dire au lieu de class Classname:
). Le principal changement consiste à unifier les types et les classes, et le bel effet secondaire de cela est qu'il vous permet d'hériter des types intégrés.
Lisez descrintro pour plus de détails.
Les nouvelles classes de style peuvent utiliser super(Foo, self)
où Foo
est une classe et self
est l'instance.
super(type[, object-or-type])
Renvoie un objet proxy qui délègue les appels de méthode à une classe parent ou frère de type. Ceci est utile pour accéder aux méthodes héritées qui ont été remplacées dans une classe. L'ordre de recherche est le même que celui utilisé par getattr () sauf que le type lui-même est ignoré.
Et dans Python 3.x, vous pouvez simplement utiliser à l' super()
intérieur d'une classe sans aucun paramètre.
type(x)
. Si je ne sous-classe pas un type intégré, il ne semble y avoir aucun avantage que je puisse voir des classes de nouveau style. Il y a un inconvénient, qui est le typage supplémentaire de(object)
.