Ich habe begonnen, das Python-Deskriptor-Protokoll ausführlicher in dem Code zu verwenden, den ich geschrieben habe. Normalerweise ist die Standard-Python-Lookup-Magie das, was ich möchte, aber manchmal finde ich, dass ich das Deskriptor-Objekt selbst anstelle der Ergebnisse seiner -Methode bekommen möchte. Der Typ des Deskriptors oder der Zugriffszustand, der im Deskriptor gespeichert ist, oder dergleichen.Python-Attribut-Lookup ohne jegliche Beschreibungszauber?
Ich schrieb den Code unten, um die Namespaces in was ich glaube, ist die richtige Reihenfolge zu gehen, und das Attribut Raw zurückgegeben, unabhängig davon, ob es ein Deskriptor ist oder nicht. Ich bin jedoch überrascht, dass ich dafür keine eingebaute Funktion oder etwas in der Standardbibliothek finden kann - ich denke, es muss da sein und ich habe es einfach nicht bemerkt oder für den richtigen Suchbegriff gegoogelt.
Gibt es irgendwo in der Python-Distribution Funktionalität, die das (oder etwas Ähnliches) schon macht?
Danke!
from inspect import isdatadescriptor
def namespaces(obj):
obj_dict = None
if hasattr(obj, '__dict__'):
obj_dict = object.__getattribute__(obj, '__dict__')
obj_class = type(obj)
return obj_dict, [t.__dict__ for t in obj_class.__mro__]
def getattr_raw(obj, name):
# get an attribute in the same resolution order one would normally,
# but do not call __get__ on the attribute even if it has one
obj_dict, class_dicts = namespaces(obj)
# look for a data descriptor in class hierarchy; it takes priority over
# the obj's dict if it exists
for d in class_dicts:
if name in d and isdatadescriptor(d[name]):
return d[name]
# look for the attribute in the object's dictionary
if obj_dict and name in obj_dict:
return obj_dict[name]
# look for the attribute anywhere in the class hierarchy
for d in class_dicts:
if name in d:
return d[name]
raise AttributeError
bearbeiten Mi, 28. Oktober 2009.
Denis Antwort gab mir eine Konvention in meiner Descriptor Klassen verwenden die Descriptor-Objekte selbst zu bekommen. Aber ich hatte eine ganze Klassenhierarchie von Descriptor-Klassen, und ich wollte nicht jede__get__
Funktion mit einem vorformulierten beginnen
def __get__(self, instance, instance_type):
if instance is None:
return self
...
dies zu vermeiden, habe ich die Wurzel des Baumes Descriptor-Klasse erbt von die folgenden:
def decorate_get(original_get):
def decorated_get(self, instance, instance_type):
if instance is None:
return self
return original_get(self, instance, instance_type)
return decorated_get
class InstanceOnlyDescriptor(object):
"""All __get__ functions are automatically wrapped with a decorator which
causes them to only be applied to instances. If __get__ is called on a
class, the decorator returns the descriptor itself, and the decorated
__get__ is not called.
"""
class __metaclass__(type):
def __new__(cls, name, bases, attrs):
if '__get__' in attrs:
attrs['__get__'] = decorate_get(attrs['__get__'])
return type.__new__(cls, name, bases, attrs)
Manchmal möchten Sie das Deskriptor-Objekt? Das verletzt die Kernerwartung für Deskriptoren: Sie sollen wie Attribute aussehen. Warum diese fundamentale Erwartung brechen? Warum das tun? Warum etwas so komplexes erstellen? –
Was ich mache, fühlt sich für mich * nicht komplex an, aber ich denke, man könnte sagen, ich experimentiere mit dem Design. In meinem aktuellen, speziellen Fall habe ich einen Deskriptor, der die Stärke einer Waffe in einem Spiel zurückgibt. Dieser Wert ist eine Funktion des Zustands des Deskriptors (Stärke der Waffe) und der Instanz (Gesundheit des Schiffes). Es gibt verschiedene Arten von Waffen; Normalerweise möchte ich nur den Wert, aber in einigen Fällen muss ich wissen, welche Art von Waffe es ist - die Art des Deskriptors. Und was, wenn ein Deskriptor Methoden hat, die nicht Teil des Deskriptor-Protokolls sind, und Sie sie aufrufen wollen? –