web-dev-qa-db-fra.com

affectation d'une variable de classe comme valeur par défaut à l'argument de méthode de classe

Je voudrais construire une méthode à l'intérieur d'une classe avec des arguments de valeurs par défaut tirés de cette classe. En général, je filtre certaines données. Dans ma classe, j'ai une méthode où normalement je passe un vecteur de données. Parfois, je n'ai pas le vecteur et je prends des données simulées. Chaque fois que je ne passe pas un vecteur particulier, je voudrais prendre des données simulées par défaut. J'ai pensé que cela devrait être une construction facile où dans ma définition de méthode, je dis a=self.vector. Mais pour une raison quelconque, j'ai une erreur NameError: name 'self' is not defined. La construction simplifiée est:

class baseClass(object):  # This class takes an initial data or simulation
    def __init__(self):
        self.x = 1
        self.y = 2

class extendedClass(baseClass): # This class does some filtering
    def __init__(self):
        baseClass.__init__(self)
        self.z = 5
    def doSomething(self, a=self.z):
        self.z = 3
        self.b = a

if __== '__main__':
    a = extendedClass()
    print a.__dict__
    a.doSomething()
    print a.__dict__

Une sortie que j'attendais devrait être:

{'y': 2, 'x': 1, 'z': 5}
{'y': 2, 'x': 1, 'z': 3, 'b': 5}

J'ai essayé l'affectation par défaut comme def doSomething(self, a=z): évidemment cela ne fonctionne jamais. Autant que je sache, self.z Est visible dans cette portée et ne devrait pas être un problème pour l'avoir comme valeur par défaut. Je ne sais pas pourquoi j'ai cette erreur et comment le faire. C'est probablement une question facile, mais j'essaie de le comprendre ou de trouver la solution sans manque depuis un certain temps déjà. J'ai trouvé similaire questions uniquement pour les autres langues.

42
tomasz74

Votre compréhension est fausse. self est lui-même un paramètre de cette définition de fonction, il n'y a donc aucun moyen qu'il soit à portée à ce stade. Ce n'est que dans la portée de la fonction elle-même.

La réponse est simplement de définir par défaut l'argument sur None, puis de vérifier cela dans la méthode:

def doSomething(self, a=None):
    if a is None:
        a = self.z
    self.z = 3
    self.b = a
50
Daniel Roseman

Voici le démontage du code pour un exemple de module simple. Un objet de code est un conteneur en lecture seule pour le bytecode, les constantes et les noms qu'il utilise, et les métadonnées sur le nombre de variables locales, la taille de pile requise, etc. Notez que tous les objets de code sont compilés en tant que constantes. Ils sont créés au moment de la compilation. Mais les objets class A Et function test Sont instanciés au moment de l'exécution (par exemple lorsque le module est importé).

Pour créer la classe, BUILD_CLASS Prend le nom 'A', Les bases Tuple(object,) Et un dict qui contient les attributs du espace de noms de classe. C'est comme instancier manuellement un type en appelant type(name, bases, dict). Pour créer le dict, une fonction est créée à partir de l'objet de code A et appelée. Enfin, l'objet classe est stocké dans l'espace de noms du module via STORE_NAME.

Dans l'objet de code A, self.z Est chargé sur la pile comme argument de MAKE_FUNCTION. L'opération de bytecode LOAD_NAME Recherchera self dans les sections locales actuelles (c'est-à-dire l'espace de noms de classe en cours de définition), les globales du module et les commandes intégrées. Cela échouera évidemment si self n'est pas défini dans la portée globale ou intégrée; il n'est clairement pas défini dans la portée locale.

Si elle réussissait, cependant, la fonction serait créée avec (self.z,) Comme attribut __defaults__, Puis stockée sous le nom local test.

>>> code = compile('''
... class A(object):
...   def test(self, a=self.z): pass
... ''', '<input>', 'exec')

>>> dis.dis(code)
  2           0 LOAD_CONST               0 ('A')
              3 LOAD_NAME                0 (object)
              6 BUILD_Tuple              1
              9 LOAD_CONST               1 (<code object A ...>)
             12 MAKE_FUNCTION            0
             15 CALL_FUNCTION            0
             18 BUILD_CLASS         
             19 STORE_NAME               1 (A)
             22 LOAD_CONST               2 (None)
             25 RETURN_VALUE

>>> dis.dis(code.co_consts[1]) # code object A
  2           0 LOAD_NAME                0 (__name__)
              3 STORE_NAME               1 (__module__)

  3           6 LOAD_NAME                2 (self)
              9 LOAD_ATTR                3 (z)
             12 LOAD_CONST               0 (<code object test ...>)
             15 MAKE_FUNCTION            1
             18 STORE_NAME               4 (test)
             21 LOAD_LOCALS         
             22 RETURN_VALUE       

@ uselpa: Votre exemple Pastebin (réécrit pour 2.x):

>>> code = compile('''
... default = 1
... class Cl(object):
...     def __init__(self, a=default):
...         print a
... Cl()
... default = 2
... Cl()
... ''', '<input>', 'exec')
>>> dis.dis(code)
  2           0 LOAD_CONST               0 (1)
              3 STORE_NAME               0 (default)

  3           6 LOAD_CONST               1 ('Cl')
              9 LOAD_NAME                1 (object)
             12 BUILD_Tuple              1
             15 LOAD_CONST               2 (<code object Cl ...>)
             18 MAKE_FUNCTION            0
             21 CALL_FUNCTION            0
             24 BUILD_CLASS         
             25 STORE_NAME               2 (Cl)

  6          28 LOAD_NAME                2 (Cl)
             31 CALL_FUNCTION            0
             34 POP_TOP             

  7          35 LOAD_CONST               3 (2)
             38 STORE_NAME               0 (default)

  8          41 LOAD_NAME                2 (Cl)
             44 CALL_FUNCTION            0
             47 POP_TOP             
             48 LOAD_CONST               4 (None)
             51 RETURN_VALUE        

Comme vous pouvez le voir, l'objet de classe Cl (et l'objet fonction __init__) N'est instancié et stocké qu'une seule fois dans le nom local 'Cl'. Le module s'exécute séquentiellement au moment de l'exécution. Par conséquent, la nouvelle liaison du nom default n'aura aucun effet sur la valeur par défaut dans __init__.

Vous pouvez instancier dynamiquement une nouvelle fonction en utilisant le code précédemment compilé et une nouvelle valeur par défaut:

>>> default = 1
>>> class Cl(object):
...     def __init__(self, a=default):
...         print a
... 

>>> from types import FunctionType
>>> default = 2
>>> Cl.__init__ = FunctionType(
...   Cl.__init__.__code__, globals(), '__init__', (default,), None)
>>> c = Cl()
2

Ceci réutilise l'objet de code déjà compilé à partir de __init__.__code__ Pour créer une fonction avec un nouveau tuple __defaults__:

>>> Cl.__init__.__defaults__
(2,)
7
Eryk Sun

Les arguments par défaut ne sont évalués qu'une seule fois, lorsque la définition est exécutée. Au lieu de cela, procédez comme suit:

def doSomething(self, a=None):
    if a is None:
        a = self.z
    self.z = 3
    self.b = a

Voir aussi http://docs.python.org/release/3.3.0/tutorial/controlflow.html#more-on-defining-functions .

5
uselpa

Cela insérera self.z si a est None/False/empty_value:

def doSomething(self, a=None):
        self.z = 3
        self.b = (a or self.z)
1
Alex