web-dev-qa-db-fra.com

héritage de str ou int

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?

49
Ruggero Turra
>>> 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'
67
John La Rooy

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 strself.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.

12
Mike Graham

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.

8
user2683246

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.

5
zoli2k

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.

4
Missing Semicolon