Frage

Ich habe angefangen, das Python-Descriptor-Protokoll zu verwenden mehr ausführlich in dem Code, den ich habe zu schreiben. Typischerweise ist die Standard-Python-Lookup-Magie, was ich passieren soll, aber manchmal mag ich finde ich das Descriptor-Objekt selbst, anstatt die Ergebnisse ihrer __get__ Methode erhalten. Wollen Sie die Art des Deskriptors wissen, oder Zugriffszustand in dem Deskriptor gespeichert, etc. zu jagen Sache.

Ich schrieb den Code unten die Namensräume in zu gehen, was ich glaube, die richtige Reihenfolge ist, und schicken Sie das Attribut roh unabhängig davon, ob es sich um ein Deskriptor ist oder nicht. Ich bin überrascht, aber, dass ich nicht eine eingebaute Funktion oder etwas in der Standardbibliothek, dies zu tun finden kann - ich meine, es dort zu sein hat, und ich habe es einfach nicht oder googeln für das richtige Suchwort bemerkt.

Gibt es Funktionalität irgendwo in der Python-Distribution, die bereits erledigt diese (oder so ähnlich)?

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 meinen Descriptor Klassen verwenden die Descriptor-Objekte selbst zu bekommen. Aber ich hatte eine ganze Klassenhierarchie von Descriptor-Klassen, und ich wollte nicht beginnen alle __get__ Funktion mit einem vorformulierten

def __get__(self, instance, instance_type):
    if instance is None: 
        return self
    ...

Um dies zu vermeiden, habe ich die Wurzel des Descriptor-Klasse Baum erben der folgenden Optionen:

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)
War es hilfreich?

Lösung

Die meisten Deskriptoren ihre Arbeit tun, wenn sie als Instanz zugegriffen Attribut nur. So ist es praktisch, sich zurück, wenn es für die Klasse zugegriffen wird:

class FixedValueProperty(object):
    def __init__(self, value):
        self.value = value
    def __get__(self, inst, cls):
        if inst is None:
            return self
        return self.value

Auf diese Weise können Sie Descriptor bekommen selbst:

>>> class C(object):
...     prop = FixedValueProperty('abc')
... 
>>> o = C()
>>> o.prop
'abc'
>>> C.prop
<__main__.FixedValueProperty object at 0xb7eb290c>
>>> C.prop.value
'abc'
>>> type(o).prop.value
'abc'

Beachten Sie, dass dies funktioniert für (die meisten?) Einbau-Deskriptoren auch:

>>> class C(object):
...     @property
...     def prop(self):
...         return 'abc'
... 
>>> C.prop
<property object at 0xb7eb0b6c>
>>> C.prop.fget
<function prop at 0xb7ea36f4>

Descriptor Zugriff nützlich sein könnte, wenn Sie benötigen Ausmaß es in Unterklasse, aber es ist ein bessere Weise dies zu tun.

Andere Tipps

Die inspect-Bibliothek stellt eine Funktion ein Attribut ohne Descriptor Magie abzurufen. inspect.getattr_static

Dokumentation: https://docs.python.org /3/library/inspect.html#fetching-attributes-statically

(Dies ist eine alte Frage, aber ich halte es für kommen, wenn sie versuchen zu erinnern, wie dies zu tun, so dass ich bin Entsendung diese Antwort, damit ich es wieder finden kann!)

Das obige Verfahren

class FixedValueProperty(object):
    def __init__(self, value):
        self.value = value
    def __get__(self, inst, cls):
        if inst is None:
            return self
        return self.value

ist eine großartige Methode, wenn Sie den Code der Immobilie kontrollieren, aber es gibt einige Fälle, wie zum Beispiel, wenn die Eigenschaft Teil einer Bibliothek ist von jemandem kontrolliert sonst, wo ein anderer Ansatz nützlich ist. Dieser alternative Ansatz kann auch in anderen Situationen, wie die Umsetzung Objektzuordnung, zu Fuß einen Namen-Raumes nützlich sein, wie in der Frage oder anderen speziellen Bibliotheken beschrieben.

Betrachten wir eine Klasse mit einer einfachen Eigenschaft:

class ClassWithProp:

    @property
    def value(self):
        return 3
>>>test=ClassWithProp()
>>>test.value
3
>>>test.__class__.__dict__.['value']
<property object at 0x00000216A39D0778>

Wenn Sie aus dem Containerobjekt Klasse zugegriffen dict , die 'Descriptor Magie' wird umgangen. Beachten Sie, dass auch, wenn wir die Eigenschaft zu einer neuen Klasse Variablen zuweisen, es verhält sich genau wie das Original mit ‚Descriptor Magie‘, aber wenn auf eine Instanz Variablen zugewiesen, so verhält sich die Eigenschaft als jedes normales Objekt und auch umgeht ‚Descriptor Magie‘.

>>> test.__class__.classvar =  test.__class__.__dict__['value']
>>> test.classvar
3
>>> test.instvar = test.__class__.__dict__['value']
>>> test.instvar
<property object at 0x00000216A39D0778>

Lassen Sie uns sagen, dass wir das Schlagwort für obj.prop wo type(obj) is C erhalten möchten.

C.prop funktioniert in der Regel, weil die Beschreiber selbst in der Regel zurück, wenn über C zugegriffen wird (das heißt, C gebunden). Aber C.prop kann einen Deskriptor in seinen Metaklasse auslösen. Wenn prop in obj nicht anwesend waren, obj.prop AttributeError erhöhen würde, während C.prop vielleicht nicht. So ist es besser inspect.getattr_static(obj, 'prop') zu verwenden.

Wenn Sie damit nicht zufrieden sind, ist hier eine CPython-spezifische Methode (von _PyObject_GenericGetAttrWithDict in Objects/object.c):

import ctypes, _ctypes

_PyType_Lookup = ctypes.pythonapi._PyType_Lookup
_PyType_Lookup.argtypes = (ctypes.py_object, ctypes.py_object)
_PyType_Lookup.restype = ctypes.c_void_p

def type_lookup(ty, name):
    """look for a name through the MRO of a type."""
    if not isinstance(ty, type):
        raise TypeError('ty must be a type')

    result = _PyType_Lookup(ty, name)
    if result is None:
        raise AttributeError(name)

    return _ctypes.PyObj_FromPtr(result)

type_lookup(type(obj), 'prop') gibt den Deskriptor in der gleichen Art und Weise, wenn CPython es bei obj.prop verwendet, wenn obj ein gewöhnliches Objekt (nicht Klasse, zum Beispiel).

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top