82 votes

Existe-t-il un moyen d'instancier une classe sans appeler __init__?

Est-il un moyen de contourner le constructeur __init__ d'une classe en python?

Exemple:

class A(object):    
    def __init__(self):
        print "FAILURE"

    def Print(self):
        print "YEHAA"

Maintenant, je voudrais créer une instance d' A. Il pourrait ressembler à ceci, cependant, cette syntaxe n'est pas correcte.

a = A
a.Print()

Merci d'avance pour votre aide.

EDIT:

Un même exemple plus complexe:

Supposons que j'ai un objet C, dont le but est de stocker un seul paramètre, et de faire quelques calculs avec elle. Le paramètre, cependant, n'est pas passé en tant que tel, mais il est intégré dans un énorme fichier de paramètres. Il pourrait ressembler à quelque chose comme ceci:

class C(object):
    def __init__(self, ParameterFile):
        self._Parameter = self._ExtractParamterFile(ParameterFile)
    def _ExtractParamterFile(self, ParameterFile):
        #does some complex magic to extract the right parameter
        return the_extracted_parameter

Je voudrais maintenant vider et charger une instance de cet objet C. Cependant, lorsque je charge cet objet, je n'ai que la seule variable self._Parameter et je ne peux pas appeler le constructeur, car il attend le fichier de paramètres.

    @staticmethod
    def Load(file):
        f = open(file, "rb")
        oldObject = pickle.load(f)
        f.close()

        #somehow create newObject without calling __init__
        newObject._Parameter = oldObject._Parameter
        return newObject

En d'autres termes, il n'est pas possible de créer une instance sans passer par le fichier de paramètres. Dans mon "réel" en cas, cependant, il n'est pas un fichier de paramètres, mais une énorme indésirable de données que j'ai certainement pas envie de transporter dans la mémoire ou même de les stocker sur un disque.

Et depuis que j'ai envie de retourner une instance de C à partir de la méthode de Load - je faire en quelque sorte avoir à appeler le constructeur.

ANCIEN EDIT:

Un exemple plus complexe, ce qui explique pourquoi je me suis poser la question:

class B(object):    
    def __init__(self, name, data):
        self._Name = name
        #do something with data, but do NOT save data in a variable

    @staticmethod
    def Load(self, file, newName):
        f = open(file, "rb")
        s = pickle.load(f)
        f.close()

        newS = B(???)
        newS._Name = newName
        return newS

Comme vous pouvez le voir, depuis data n'est pas stockée dans une variable de classe, je ne peut pas passer à d' __init__. Bien sûr, je pourrais simplement les stocker, mais que faire si les données sont un énorme objet, que je n'ai pas envie de transporter en mémoire tout le temps ou même l'enregistrer sur le disque?

78voto

Rosh Oxymoron Points 6965

Vous pouvez contourner __init__ en appelant __new__ directement. Ensuite, vous pouvez créer un objet d'un type donné et appeler une méthode alternative pour __init__. C'est quelque chose qu' pickle le ferait.

Toutefois, d'abord, je tiens à souligner que c'est quelque chose que vous ne devriez pas faire et tout ce que vous voulez réaliser, il ya de meilleures façons de le faire, dont certains ont été mentionnés dans les autres réponses. En particulier, c'est une mauvaise idée de sauter appelant __init__.

Lorsque les objets sont créés, plus ou moins ce qui se passe:

a = A.__new__(A, *args, **kwargs)
a.__init__(*args, **kwargs)

Vous pouvez passer à la deuxième étape.

Voici pourquoi vous ne devriez pas le faire: Le but de l' __init__ est d'initialiser l'objet, remplir tous les champs et de s'assurer que l' __init__ méthodes des classes parent sont aussi appelés. Avec pickle il est une exception car elle tente de stocker toutes les données associées à l'objet (y compris tous les champs/variables d'instance sont définies pour l'objet), et donc tout ce qui a été définie par l' __init__ la fois précédente serait restauré par cornichon, il n'y a pas besoin de l'appeler à nouveau.

Si vous passez __init__ et l'utilisation alternative de l'initialiseur, vous auriez une sorte de duplication de code - il y aurait deux endroits où les variables d'instance sont remplis, et il est facile de rater l'un d'entre eux dans l'une des initialiseurs ou accidentellement faire les deux à remplir les champs d'agir différemment. Cela donne la possibilité de bogues subtils qui ne sont pas triviales à la trace (vous auriez à savoir qui de l'initialiseur a été appelé), et le code sera plus difficile à maintenir. Pour ne pas mentionner que vous seriez dans une encore plus grande confusion, si vous êtes en utilisant l'héritage - les problèmes vont aller jusqu'à la chaîne d'héritage, parce que vous auriez à utiliser cette alternative initialiseur de partout jusqu'à la chaîne.

Aussi, ce faisant, vous seriez plus ou moins impérieuses Python de la création de l'instance et de faire votre propre. Python déjà fait pour vous assez bien, pas besoin d'aller réinventer et il va embrouiller les gens à l'aide de votre code.

Voici ce qu'il faut mieux le faire à la place: l'Utilisation d'un seul __init__ méthode qui sera appelée pour tous les instanciations de la classe qui initialise toutes les variables d'instance correctement. Pour les différents modes de l'initialisation, utilisez l'une des deux approches:

  1. Support des signatures différentes pour __init__ qui gèrent votre cas en utilisant des arguments optionnels.
  2. Créer plusieurs méthodes de la classe qui servent de rechange des constructeurs. Assurez-vous qu'ils créent des instances de la classe de façon normale (c'est à dire en appelant __init__), comme illustré par Romain Bodnarchuk, lors de l'exécution de travaux supplémentaires ou quoi que ce soit. Il est préférable si elles passent toutes les données de la classe et, __init__ poignées), mais si c'est impossible ou peu pratique, vous pouvez définir des variables d'instance après que l'instance a été créée et __init__ se fait en cours d'initialisation.

Si __init__ a une étape facultative (par exemple, comme le traitement qu' data argument, bien que vous devriez être plus précis), vous pouvez soit en faire un argument optionnel ou de faire une méthode normale que le traitement... ou les deux.

24voto

Roman Bodnarchuk Points 12136

Utilisez classmethod décorateur pour votre méthode Load :

 class B(object):    
    def __init__(self, name, data):
        self._Name = name
        #store data

    @classmethod
    def Load(cls, file, newName):
        f = open(file, "rb")
        s = pickle.load(f)
        f.close()

        return cls(newName, s)
 

Vous pouvez donc faire:

 loaded_obj = B.Load('filename.txt', 'foo')
 

Modifier:

Quoi qu'il en soit, si vous souhaitez toujours omettre la méthode __init__ , essayez __new__ :

 >>> class A(object):
...     def __init__(self):
...             print '__init__'
...
>>> A()
__init__
<__main__.A object at 0x800f1f710>
>>> a = A.__new__(A)
>>> a
<__main__.A object at 0x800f1fd50>
 

11voto

user8335 Points 51

En prenant votre question à la lettre, j'utiliserais des méta-classes:

 class MetaSkipInit(type):
    def __call__(cls):
        return cls.__new__(cls)


class B(object):
    __metaclass__ = MetaSkipInit

    def __init__(self):
        print "FAILURE"

    def Print(self):
        print "YEHAA"


b = B()
b.Print()
 

Cela peut être utile, par exemple pour copier des constructeurs sans polluer la liste des paramètres. Mais le faire correctement serait plus de travail et de soins que mon hack proposé.

10voto

Ryan Hiebert Points 257

Pas vraiment. Le but de l' __init__ est d'instancier un objet, et par défaut, il n'a vraiment pas de faire quoi que ce soit. Si l' __init__ méthode n'est pas de faire ce que vous voulez, et ce n'est pas votre propre code à modifier, vous pouvez choisir de l'activer si. Par exemple, en prenant votre classe, nous avons pu effectuer les opérations suivantes pour éviter l'appel qu' __init__ méthode:

def emptyinit(self):
    pass
A.__init__ = emptyinit
a = A()
a.Print()

Cela permettra de passer dynamiquement qui __init__ méthode de la classe, pour le remplacer par un vide d'appel. Notez que ce n'est probablement PAS une bonne chose à faire, qu'il n'appelle pas la super classe __init__ méthode.

Vous pouvez également sous-classe pour créer votre propre classe qui fait tout de même, à l'exception de substitution de l' __init__ méthode pour faire ce que vous voulez qu'il (peut-être rien).

Peut-être, toutefois, vous souhaitez tout simplement appeler la méthode de la classe sans l'instanciation d'un objet. Si c'est le cas, vous devriez regarder dans l' @classmethod et @staticmethod décorateurs. Ils permettent uniquement pour ce type de comportement.

Dans votre code, vous avez mis le @staticmethod décorateur, qui n'est pas un self argument. Peut-être ce qui peut être mieux pour le but de @classmethod, qui pourrait ressembler à ceci:

@classmethod
def Load(cls, file, newName):
    # Get the data
    data = getdata()
    # Create an instance of B with the data
    return cls.B(newName, data)

Mise à JOUR: Rosh Excellente réponse indique que vous POUVEZ éviter d'appeler __init__ par la mise en œuvre de __new__, ce qui en réalité, j'étais pas au courant de (bien qu'il est parfaitement logique). Grâce Rosh!

9voto

xbb Points 470

J'ai lu le Python cookbook et il y a une section de parler de cela: l'exemple est donné à l'aide de __new__ de contourner __init__()

>>> class A:
 def __init__(self,a):
 auto.a = a


>>> test = A('A')
>>> de test.un
'a'
>>> test_noinit = A.__new__(A)
>>> test_noinit.un
Traceback (most recent call last):
 File "", line 1, in 
test_noinit.un
AttributeError: 'A' de l'objet n'a pas d'attribut 'a'
>>> 

Cependant, je pense que cela ne fonctionne que dans Python3. Ci-dessous est en cours d'exécution en vertu de la 2.7

>>> class A:
 def __init_(self,a):
 auto.a = a


>>> test = A.__new__(A)

Traceback (most recent call last):
 File "", line 1, in 
 test = A.__new__(A)
AttributeError: classe A n'a pas d'attribut '__new__'
>>> 

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