Inspektion von Pythonenklassenattributen

Ich brauche eine Möglichkeit, eine Klasse zu inspizieren, damit ich sicher identifizieren kann welche Attribute benutzerdefinierte Klassenattribute sind. Das Problem ist, dass Funktionen wie dir (), inspect.getmembers () und Freunde alle Klassenattribute zurückgeben, einschließlich der vordefinierten wie: __class__ , __doc__ , __dict__ , __hash__ . Das ist natürlich verständlich, und man könnte argumentieren, dass ich nur eine Liste namhafter Mitglieder ignorieren kann, aber leider sind diese vordefinierten Attribute verpflichtet, sich mit verschiedenen Versionen von Python zu ändern und damit mein Projekt im Python-Projekt zu ändern – und das gefällt mir nicht

Beispiel:

 >>> class A: ... a=10 ... b=20 ... def __init__(self): ... self.c=30 >>> dir(A) ['__doc__', '__init__', '__module__', 'a', 'b'] >>> get_user_attributes(A) ['a','b'] 

Im obigen Beispiel möchte ich einen sicheren Weg, um nur die benutzerdefinierten Klassenattribute ['a', 'b'] nicht 'c' abzurufen, da es sich um ein Instanzattribut handelt. Also meine Frage ist … Kann mir jemand mit der obigen fiktiven Funktion get_user_attributes(cls) helfen?

PS Ich habe einige Zeit damit verbracht, das Problem zu lösen, indem ich die Klasse auf AST-Ebene analysiere, was sehr einfach wäre. Aber ich kann keine Möglichkeit finden, bereits geparste Objekte in einen AST-Knotenbaum umzuwandeln. Ich denke, alle AST-Infos werden verworfen, sobald eine Klasse in Bytecode kompiliert wurde.

Mit freundlichen Grüßen Jakob

4 Solutions collect form web for “Inspektion von Pythonenklassenattributen”

Unten ist der harte Weg. Hier ist der einfache Weg Ich weiß nicht, warum es mir nicht früher passiert ist.

 import inspect def get_user_attributes(cls): boring = dir(type('dummy', (object,), {})) return [item for item in inspect.getmembers(cls) if item[0] not in boring] 

Hier ist ein Anfang

 def get_user_attributes(cls): boring = dir(type('dummy', (object,), {})) attrs = {} bases = reversed(inspect.getmro(cls)) for base in bases: if hasattr(base, '__dict__'): attrs.update(base.__dict__) elif hasattr(base, '__slots__'): if hasattr(base, base.__slots__[0]): # We're dealing with a non-string sequence or one char string for item in base.__slots__: attrs[item] = getattr(base, item) else: # We're dealing with a single identifier as a string attrs[base.__slots__] = getattr(base, base.__slots__) for key in boring: del attrs['key'] # we can be sure it will be present so no need to guard this return attrs 

Das sollte ziemlich robust sein. Im Wesentlichen funktioniert es, indem man die Attribute, die auf einer Standard-Unterklasse des object zu ignorieren sind. Es bekommt dann die mro der Klasse, die an sie weitergegeben wird und durchquert es in umgekehrter Reihenfolge, so dass Unterschlüssel Schlüssel überschüssige Schlüssel überschreiben können. Es gibt ein Wörterbuch von Schlüsselwertpaaren zurück. Wenn du eine Liste von Schlüssel, Wert Tupel wie in inspect.getmembers dann einfach entweder attrs.items() oder list(attrs.items()) in Python 3.

Wenn du den Mro nicht wirklich durchqueren willst und einfach nur Attribute direkt auf der Unterklasse definieren willst, dann ist es einfacher:

 def get_user_attributes(cls): boring = dir(type('dummy', (object,), {})) if hasattr(cls, '__dict__'): attrs = cls.__dict__.copy() elif hasattr(cls, '__slots__'): if hasattr(base, base.__slots__[0]): # We're dealing with a non-string sequence or one char string for item in base.__slots__: attrs[item] = getattr(base, item) else: # We're dealing with a single identifier as a string attrs[base.__slots__] = getattr(base, base.__slots__) for key in boring: del attrs['key'] # we can be sure it will be present so no need to guard this return attrs 

Doppelte Unterstriche an beiden Enden von "speziellen Attributen" sind ein Teil von Python vor 2.0 gewesen. Es wäre sehr unwahrscheinlich, dass sie das jederzeit in naher Zukunft ändern würden.

 class Foo(object): a = 1 b = 2 def get_attrs(klass): return [k for k in Foo.__dict__.keys() if not k.startswith('__') and not k.endswith('__')] print get_attrs(Foo) 

['A', 'b'

Vielen Dank Aaronasterling, du hast mir den Ausdruck gegeben, den ich brauchte 🙂 Meine letzte Klasse Attribut Inspektor Funktion sieht so aus:

 def get_user_attributes(cls,exclude_methods=True): base_attrs = dir(type('dummy', (object,), {})) this_cls_attrs = dir(cls) res = [] for attr in this_cls_attrs: if base_attrs.count(attr) or (callable(getattr(cls,attr)) and exclude_methods): continue res += [attr] return res 

Entweder geben Sie nur die Klassenattribute Variabels zurück (exclude_methods = True) oder rufen Sie auch die Methoden ab. Meine ersten Tests in der oben genannten Funktion unterstützt sowohl alte als auch neue Stil-Python-Klassen.

/ Jakob

Wenn du neue Style-Klassen benutzt, kannst du einfach die Attribute der übergeordneten Klasse subtrahieren?

 class A(object): a = 10 b = 20 #... def get_attrs(Foo): return [k for k in dir(Foo) if k not in dir(super(Foo))] 

Bearbeiten: Nicht ganz. __dict__ , __module__ und __weakref__ erscheinen beim Erben vom Objekt, sind aber nicht im Objekt selbst vorhanden. Sie könnten Sonderfälle diese – ich bezweifle, dass sie sich sehr oft ändern würden.

Python ist die beste Programmiersprache der Welt.