2013-07-03 3 views
0

J'ai ce problème générique en python. La classe de base définit un attribut de classe class_attr. Cet attribut est immuable, dans ce cas c'est un nombre. Je veux changer cet attribut d'une classe dérivée, reliant ainsi Base.class_attr à la nouvelle valeur (dans mon cas de jouet, l'incrémentant).Rebobinage d'un attribut de classe immutable dans la classe de base à partir d'une classe dérivée

La question est comment faire ceci sans nommer explicitement Base dans l'instruction Base.class_attr += 1.

class Base(object): 
    # class attribute: 
    class_attr = 0 

class Derived(Base): 
    @classmethod 
    def increment_class_attr(cls):   
     Base.class_attr += 1 
     # is there a solution which does not name the owner of the 
     # class_attr explicitly? 

     # This would cause the definition of Derived.class_attr, 
     # thus Base.class_attr and Derived.class_attr would be 
     # two independent attributes, no more in sync: 
    # cls.class_attr += 1 

Derived.increment_class_attr() 
Derived.increment_class_attr() 

print Base.class_attr # 2 

S'il vous plaît noter: Je suis après la question même, c'est-ce que je peux rebind les attributs d'une classe parente. Je ne suis pas après les solutions de contournement à ce problème (par exemple, déplacer increment_class_attr à la base).

Répondre

1

Utilisez l'attribut __bases__:

In [68]: class Base(object): 
    ...:  # class attribute: 
    ...:  class_attr = 0 
    ...:  

In [69]: class Derived(Base): 
    ...:  @classmethod 
    ...:  def inc(cls): 
    ...:   p, = cls.__bases__ 
    ...:   p.class_attr += 1 
    ...:   

In [70]: Base.class_attr 
Out[70]: 0 

In [71]: Derived.inc() 

In [72]: Derived.inc() 

In [73]: Base.class_attr 
Out[73]: 2 

Si vous avez l'héritage multiple:

In [88]: class DifferentInherited(object): 
    ...:  class2_attr = 0 
    ...: 


In [90]: class Der2(Base, DifferentInherited): 
    ...:  @classmethod 
    ...:  def inc(cls): 
    ...:   print cls.__bases__ 
    ...:   a, b, = cls.__bases__ 
    ...:   print a.class_attr 
    ...:   print b.class2_attr 
    ...:   

In [91]: Der2.inc() 
(<class '__main__.Base'>, <class '__main__.DifferentInherited'>) 
2 
0 

En supposant que vous ne connaissez pas l'ordre de succession soit, vous aurez besoin de tester chaque classe pour la variable:

In [127]: class Der3(DifferentInherited, Base): 
    ...:  @classmethod 
    ...:  def inc(cls): 
    ...:   # This gets a list of *all* classes with the attribute `class_attr` 
    ...:   classes = [c for c in cls.__bases__ if 'class_attr' in c.__dict__] 
    ...:   for c in classes: 
    ...:    c.class_attr += 1 
    ...:    

In [128]: Der3.inc() 

In [129]: Base.class_attr 
Out[129]: 3 

In [131]: DifferentInherited.class2_attr 
Out[131]: 0 

Et l'héritage multiple utilise __mro__:

In [146]: class Multi(Der3): 
    ...:  @classmethod 
    ...:  def inc(cls): 
    ...:   c_attr = [c for c in cls.__mro__ if 'class_attr' in c.__dict__] 
    ...:   print c_attr 
    ...:   c_attr[0].class_attr += 1 
    ...:   

In [147]: Multi.inc() 
[<class '__main__.Base'>] 

In [148]: Base.class_attr 
Out[148]: 4 
+0

Merci, mais cela a à peu près le même niveau de maintenabilité que l'original avec 'Base'. Qu'en est-il si j'hérite Dérivé multiplier 'Derived (AnotherBase, Base)' ou à travers plusieurs héritage simple 'Derived1 (Base)'; 'Dérivé (Derived1)'? – ondrejdee

+0

'__bases__' et' Base' n'ont rien en commun. '__bases__' fait référence aux classes héritées. Il retourne un tuple de classes: 'p, = cls .__ bases__' obtient le premier (La première classe héritée - dans ce cas,' Base'.) – TyrantWave

+0

Oui j'ai compris votre code, ce que je dis c'est que quand j'en ai un autre baseclass nommée 'AnotherBase' et déclarez une classe dérivée' class Derived (AnotherBase, Base) 'alors votre variable' p' ne se rapportera plus à 'Base' mais' AnotherBase', mais 'AnotherBase' n'est pas le propriétaire de la classe De même, avec l'héritage chaîné, 'p' se référera à' Derived1', pas à 'Derived' – ondrejdee

Questions connexes