Mais qui donc hérite de ma classe ?


Pas une question que l’on se pose souvent, à moins d’avoir une lib qui expose une classe avec beaucoup d’introspection.

Ceci dit pour la culture G, je me suis dit que ça pourrait intéresser quelques geeks de savoir comment lister les enfants d’une classe, récursivement.

def subclasses(cls, _found=()):
    """ Retourne toutes les classes héritant d'une classe """
 
    # On check que la classe est hérite bien de "object".
    # Utile uniquement pour un code 2.x qui utiliserait
    # des old style classes, qui n'ont pas "__subclasses__".
    if not isinstance(cls, type):
      raise TypeError('subclasses works only with new-style classes'
                            ', not %s' % cls)
 
    # On va stocker les classes rencontrées dans un set
    # pour éviter les doublons car on peut tomber sur
    # un héritage en forme de diamant.
    _found = _found or set()
 
    try:
      subs = cls.__subclasses__()
    except TypeError:  # Erreur levée si cls == type, petits vicieux
      subs = cls.__subclasses__(cls)
 
    for sub in subs:
        if sub not in _found:
            _found.add(sub)
            # Un appel récursif comme on les aimes
            subclasses(sub, _found)
 
    return _found

Pas très compliqué, donc, si on connait les piègeounets : ça ne marche pas sur les old syle classes, et il y a un traitement spécial pour type. Celà dit, qui utilise encore des old styles classes ? Et franchement lister les héritiers de type, à par pour satisfaire sa curiosité…

Aller, juste pour le fun, sur un virtualenv vierge:

for x in subclasses(object):
    print x
## <type 'callable-iterator'>
## <class '_abcoll.Sized'>
## <type 'exceptions.EOFError'>
## <class '_abcoll.Mapping'>
## <type 'exceptions.UnicodeWarning'>
## <type 'exceptions.IOError'>
## <type 'memoryview'>
## <type 'code'>
## <type 'exceptions.ImportWarning'>
## <type 'sys.flags'>
## <type 'iterator'>
## <class 'warnings.catch_warnings'>
## <type 'exceptions.Warning'>
## <type 'exceptions.EnvironmentError'>
## <type 'dict'>
## <class 'encodings.utf_8.IncrementalEncoder'>
## <type 'weakproxy'>
## <class '_abcoll.Iterator'>
## <type 'sys.version_info'>
## <type 'posix.stat_result'>
## <type 'frozenset'>
## <class 'codecs.CodecInfo'>
## <class 'zipimport.ZipImportError'>
## <type 'exceptions.StopIteration'>
## <type 'exceptions.ImportError'>
## <class 'site._Printer'>
## <type 'weakref'>
## <class '_abcoll.Sequence'>
## <type 'property'>
## <class 'encodings.utf_8.IncrementalDecoder'>
## <type 'imp.NullImporter'>
## <type 'exceptions.TypeError'>
## <type 'bytearray'>
## <type 'int'>
## <type 'exceptions.UnicodeTranslateError'>
## <type 'instance'>
## <type 'exceptions.BaseException'>
## <type 'exceptions.SyntaxWarning'>
## <type 'file'>
## <class '_abcoll.MappingView'>
## <class '_weakrefset._IterationGuard'>
## <type 'exceptions.UnicodeDecodeError'>
## <type 'classobj'>
## <type 'exceptions.UnicodeError'>
## <class 'abc.abstractproperty'>
## <type 'exceptions.DeprecationWarning'>
## <type 'complex'>
## <type 'set'>
## <type 'buffer'>
## <type 'generator'>
## <type 'exceptions.Exception'>
## <class 'codecs.IncrementalDecoder'>
## <type 'exceptions.UserWarning'>
## <type 'staticmethod'>
## <type 'exceptions.UnicodeEncodeError'>
## <class 'encodings.CodecRegistryError'>
## <class '_abcoll.MutableSequence'>
## <class '_abcoll.KeysView'>
## <type 'cell'>
## <type 'exceptions.StandardError'>
## <type 'enumerate'>
## <type 'exceptions.KeyboardInterrupt'>
## <type 'exceptions.BufferError'>
## <class 'warnings.WarningMessage'>
## <type 'EncodingMap'>
## <class 'codecs.BufferedIncrementalDecoder'>
## <type 'exceptions.ValueError'>
## <class '_abcoll.Set'>
## <type 'exceptions.MemoryError'>
## <type 'exceptions.SystemExit'>
## <type 'reversed'>
## <class '_abcoll.Hashable'>
## <type 'bool'>
## <type 'classmethod'>
## <type 'exceptions.ReferenceError'>
## <type 'exceptions.GeneratorExit'>
## <class '_abcoll.Container'>
## <class '_abcoll.ItemsView'>
## <type 'xrange'>
## <type 'exceptions.PendingDeprecationWarning'>
## <type 'basestring'>
## <type 'exceptions.SystemError'>
## <type 'exceptions.ZeroDivisionError'>
## <class 'site.Quitter'>
## <type 'long'>
## <type 'NotImplementedType'>
## <type 'super'>
## <type 'fieldnameiterator'>
## <class '_abcoll.Callable'>
## <class '_abcoll.MutableSet'>
## <type 'exceptions.ArithmeticError'>
## <class 'abc.ABCMeta'>
## <type 'exceptions.OverflowError'>
## <type 'exceptions.FutureWarning'>
## <type 'sys.float_info'>
## <type 'formatteriterator'>
## <type 'type'>
## <type 'exceptions.AssertionError'>
## <type 'traceback'>
## <type 'exceptions.FloatingPointError'>
## <type 'function'>
## <type 'instancemethod'>
## <type 'sys.long_info'>
## <type 'builtin_function_or_method'>
## <class 'signal.ItimerError'>
## <class 'site._Helper'>
## <type 'exceptions.LookupError'>
## <type 'wrapper_descriptor'>
## <type 'exceptions.KeyError'>
## <class '_abcoll.ValuesView'>
## <type 'slice'>
## <class '_weakrefset.WeakSet'>
## <type 'list'>
## <type 'exceptions.IndexError'>
## <type 'getset_descriptor'>
## <type 'posix.statvfs_result'>
## <type 'exceptions.SyntaxError'>
## <type 'frame'>
## <class 'codecs.BufferedIncrementalEncoder'>
## <type 'PyCapsule'>
## <type 'exceptions.IndentationError'>
## <type 'NoneType'>
## <type 'zipimport.zipimporter'>
## <type 'member_descriptor'>
## <type 'exceptions.AttributeError'>
## <type 'ellipsis'>
## <type 'exceptions.UnboundLocalError'>
## <type 'unicode'>
## <class '_abcoll.Iterable'>
## <type 'exceptions.TabError'>
## <type 'exceptions.NameError'>
## <type 'tuple'>
## <class 'warnings._OptionError'>
## <type 'exceptions.NotImplementedError'>
## <type 'exceptions.RuntimeWarning'>
## <type 'str'>
## <class '_abcoll.MutableMapping'>
## <type 'exceptions.BytesWarning'>
## <type 'module'>
## <type 'exceptions.RuntimeError'>
## <class 'codecs.IncrementalEncoder'>
## <type 'float'>
## <type 'exceptions.OSError'>
## <type 'dictproxy'>
## <type 'weakcallableproxy'>

Putain, ça c’est du remplissage ! 20minutes serait fier de moi.

6 thoughts on “Mais qui donc hérite de ma classe ?

  • herison

    Yep,

    class A(object):
    pass

    class B(A):
    pass

    class C(B):
    pass

    print(type.mro(C))
    [, , , ]

  • Kermit

    @herison : il me semble que Sam propose l’inverse :
    trouver toutes les classes qui m’ont comme ancêtre
    type.mro() ou C.__mro__ propose la liste des ancêtres

  • foX
    def subclasses(cls, _found=()):

    Un mutable en valeur par défaut c’est un coup à faire une connerie non ?

  • kontre

    Sauf qu’un tuple c’est pas mutable ! Et comme il est vide sa valeur booleenne est False. Moi j’aurais mis None pour plus de clarté mais ça ne change pas le programme.

Comments are closed.

Des questions Python sans rapport avec l'article ? Posez-les sur IndexError.