116 votes

Initialiser automatiquement les variables d'instance ?

J'ai une classe python qui ressemble à ceci :

class Process:
    def __init__(self, PID, PPID, cmd, FDs, reachable, user):

suivi par :

        self.PID=PID
        self.PPID=PPID
        self.cmd=cmd
        ...

Existe-t-il un moyen d'auto-initialiser ces variables d'instance, comme la liste d'initialisation du C++ ? Cela permettrait d'éviter beaucoup de code redondant.

1 votes

Voir également la discussion sur le autoassign recette d'activation, et une recette alternative autoargs mise en œuvre à : Quelle est la meilleure façon de faire de l'attribution automatique d'attributs en Python, et est-ce une bonne idée ? - Stack Overflow

123voto

Nadia Alramli Points 40381

Vous pouvez utiliser un décorateur :

from functools import wraps
import inspect

def initializer(func):
    """
    Automatically assigns the parameters.

    >>> class process:
    ...     @initializer
    ...     def __init__(self, cmd, reachable=False, user='root'):
    ...         pass
    >>> p = process('halt', True)
    >>> p.cmd, p.reachable, p.user
    ('halt', True, 'root')
    """
    names, varargs, keywords, defaults = inspect.getargspec(func)

    @wraps(func)
    def wrapper(self, *args, **kargs):
        for name, arg in list(zip(names[1:], args)) + list(kargs.items()):
            setattr(self, name, arg)

        for name, default in zip(reversed(names), reversed(defaults)):
            if not hasattr(self, name):
                setattr(self, name, default)

        func(self, *args, **kargs)

    return wrapper

Utilisez-le pour décorer le __init__ método:

class process:
    @initializer
    def __init__(self, PID, PPID, cmd, FDs, reachable, user):
        pass

Sortie :

>>> c = process(1, 2, 3, 4, 5, 6)
>>> c.PID
1
>>> dir(c)
['FDs', 'PID', 'PPID', '__doc__', '__init__', '__module__', 'cmd', 'reachable', 'user'

1 votes

Petit problème - vous avez oublié d'importer l'inspection.

6 votes

Cela fonctionne et répond à la question donc j'ai voté pour. Mais j'ai gardé la réponse de Ferdidand Beyer : "L'explicite est meilleur que l'implicite"

17 votes

+1 Pour cette excellente réponse qui a résolu mon problème. Mais ne devrait-il pas s'agir d'une fonctionnalité essentielle du langage ? Pensez-vous que cela vaille la peine d'écrire un PEP ?

37voto

Kiv Points 9116

Si vous utilisez Python 2.6 ou supérieur, vous pouvez utiliser collections.namedtuple :

>>> from collections import namedtuple
>>> Process = namedtuple('Process', 'PID PPID cmd')
>>> proc = Process(1, 2, 3)
>>> proc.PID
1
>>> proc.PPID
2

Ceci est particulièrement approprié lorsque votre classe n'est en fait qu'un grand sac de valeurs.

2 votes

"Ceci est approprié surtout lorsque votre classe n'est en fait qu'un gros sac de valeurs." Dans un tel scénario, vous pourriez aussi faire ceci : https://docs.python.org/3.3/tutorial/classes.html#odds-and-ends

30voto

Ferdinand Beyer Points 27723

En citant le Zen du python ,

L'explicite est préférable à l'implicite.

14 votes

Une déclaration de liste d'initialisation ne serait-elle pas suffisamment explicite ?

0 votes

Je suppose. Mais je ne vois pas de raison d'ajouter une telle chose à la langue. Je préfère nettement les déclarations d'affectation multiples à une sorte de magie de décorateur en coulisse.

34 votes

@Ferdinand, je suis d'accord qu'il serait stupide d'avoir dans le langage quelque chose qui peut parfaitement être dans la stdlib, mais, il DEVRAIT être dans la stdlib, parce que "le beau est mieux que le laid" prend le dessus, et beaucoup d'affectations répétitives sont laides (comme toute forme de répétition).

29voto

Jochen Ritzel Points 42916

Une autre chose que vous pouvez faire :

class X(object):
    def __init__(self, a,b,c,d):
        vars = locals() # dict of local names
        self.__dict__.update(vars) # __dict__ holds and object's attributes
        del self.__dict__["self"] # don't need `self`

Mais la seule solution que je recommande, en plus de l'épeler, est de "faire une macro dans votre éditeur" ;-p

1 votes

Bien vu d'avoir supprimé "self".

15voto

SilentGhost Points 79627

Vous pourriez le faire facilement avec les arguments des mots-clés, par exemple comme ceci :

>>> class D:
    def __init__(self, **kwargs):
        for k, v in kwargs.items():
            setattr(self, k, v)

>>> D(test='d').test
'd'

Une implémentation similaire pour les arguments positionnels serait :

>> class C:
    def __init__(self, *args):
        self.t, self.d = args

>>> C('abc', 'def').t
'abc'
>>> C('abc', 'def').d
'def'

ce qui, pour moi, ne semble pas résoudre votre problème.

3 votes

Une autre variation que j'aime bien est self.__dict__.update( **kwargs )

0 votes

Autant utiliser locals() et mettre des arguments normaux.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X