Pourquoi j'ai du mal à créer une classe héritant de str (ou aussi de int)
class C(str):
def __init__(self, a, b):
str.__init__(self,a)
self.b = b
C("a", "B")
TypeError: str() takes at most 1 argument (2 given)
c'est la même chose si j'essaie d'utiliser int
au lieu de str
, mais cela fonctionne avec des classes personnalisées. Je dois utiliser __new__
au lieu de __init__
? Pourquoi?
>>> class C(str):
... def __new__(cls, *args, **kw):
... return str.__new__(cls, *args, **kw)
...
>>> c = C("hello world")
>>> type(c)
<class '__main__.C'>
>>> c.__class__.__mro__
(<class '__main__.C'>, <type 'str'>, <type 'basestring'>, <type 'object'>)
Puisque __init__
est appelé après la construction de l'objet, il est trop tard pour modifier la valeur des types immuables. Notez que __new__
est une méthode de classe, j'ai donc appelé le premier paramètre cls
Voir ici pour plus d'informations
>>> class C(str):
... def __new__(cls, value, meta):
... obj = str.__new__(cls, value)
... obj.meta = meta
... return obj
...
>>> c = C("hello world", "meta")
>>> c
'hello world'
>>> c.meta
'meta'
L'héritage de types intégrés en vaut rarement la peine. Vous devez faire face à plusieurs problèmes et vous n'obtenez pas vraiment beaucoup d'avantages.
Il est presque toujours préférable d'utiliser la composition. Au lieu d'hériter de str
, vous conserveriez un objet str
comme attribut.
class EnhancedString(object):
def __init__(self, *args, **kwargs):
self.s = str(*args, **kwargs)
vous pouvez différer toutes les méthodes que vous souhaitez utiliser sur le str
self.s
sous-jacent manuellement ou automatiquement à l'aide de __getattr__
.
Cela étant dit, avoir besoin de votre propre type de chaîne est quelque chose qui devrait vous donner une pause. Il existe de nombreuses classes qui devraient stocker une chaîne en tant que données principales, mais vous souhaitez généralement utiliser str
ou unicode
(cette dernière si vous représentez du texte) pour la représentation générale des chaînes. (Une exception courante est si vous devez utiliser le type de chaîne d'une boîte à outils d'interface utilisateur.) Si vous souhaitez ajouter des fonctionnalités à vos chaînes, essayez si vous le pouvez d'utiliser fonctions qui opèrent sur les chaînes plutôt que - nouveaux objets pour servir de chaînes, ce qui rend votre code plus simple et plus compatible avec les programmes de tous les autres.
Lorsque vous instanciez une classe, les arguments que vous transmettez sont transmis aux méthodes __new__
(Constructeur) puis __init__
(Initialiseur) de la classe. Donc, si vous héritez d'une classe qui a des restrictions sur le nombre d'arguments pouvant être fournis lors de l'instanciation, vous devez garantir que ni son __new__
, Ni son __init__
N'obtiendront plus d'arguments qu'ils ne le pensent. avoir. Voilà donc le problème que vous avez. Vous instanciez votre classe avec C("a", "B")
. L'interpréteur recherche la méthode __new__
Dans C
. C
ne l'a pas, donc python jette un œil dans sa classe de base str
. Et comme il en a une, celle-ci est utilisée et fournie avec les deux mais str.__new__
s'attend à obtenir un seul argument (en plus de son objet classe comme premier argument). Donc TypeError
est levé. C'est pourquoi vous devez l'étendre dans votre classe enfant de la même manière que vous faites avec __init__
. Mais gardez à l'esprit qu'il doit retourner une instance de classe et qu'il s'agit d'une méthode statique (qu'elle soit définie avec @staticmethod
décorateur ou pas) qui compte si vous utilisez la fonction super
.
Utilisation __new__
en cas de types immuables:
class C(str):
def __new__(cls, content, b):
return str.__new__(cls, content)
def __str__(self):
return str.__str__(self)
a=C("hello", "world")
print a
print renvoie hello
.
Les chaînes Python sont des types immuables. La fonction __new__
est appelé pour créer une nouvelle instance de l'objet C
. Le python __new__
La fonction existe essentiellement pour permettre l'héritage de types immuables.
Après avoir lu attentivement this , voici une autre tentative de sous-classer str. Le changement par rapport aux autres réponses crée l'instance dans la classe correcte en utilisant super(TitleText, cls).__new__
. Celui-ci semble se comporter comme un str chaque fois qu'il est utilisé, mais m'a permis de remplacer une méthode:
class TitleText(str):
title_text=""
def __new__(cls,content,title_text):
o=super(TitleText, cls).__new__(cls,content)
o.title_text = title_text
return o
def title(self):
return self.title_text
>>> a=TitleText('name','A Nice name')
>>> a
'name'
>>> a[0]
'n'
>>> a[0:2]
'na'
>>> a.title()
'A Nice name'
Cela vous permet de découper et d'indexer correctement. À quoi ça sert? Pour renommer l'application Django dans la page d'index d'administration.