Looking for python attributes without descriptor magic?

I started using the python descriptor protocol in more detail in the code I wrote. Typically, the default search magic for python is what I want, but sometimes I find that I want to get the handle object itself, and not the results of its __get__ method. Desire to find out the type of descriptor or access state stored in the descriptor, or something similar.

I wrote the code below to skip namespaces in what I think is right and to return the original attribute regardless of whether it is a descriptor or not. I am surprised, although I cannot find a built-in function or something in the standard library to do this - I believe that it should be there, and I just did not notice it or searched for the correct search.

Is there any function in the python distribution that already does this (or something similar)?

Thanks!

 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 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 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 

Edited Wed, Oct 28, 2009.

Denis's answer gave me an agreement on using a descriptor in my classes to get the descriptor objects themselves. But I had a whole hierarchy of class descriptor classes, and I did not want to start every __get__ function with a template

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

To avoid this, I made the root of the descriptor class tree inherited from the following:

 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) 
+6
python python-datamodel descriptor
source share
4 answers

Most descriptors do their job when accessed only as an attribute of an instance. Thus, it is convenient to return itself when accessing the class:

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

This allows you to get a handle:

 >>> 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' 

Note that this works for (most?) Inline descriptors:

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

Accessing the descriptor can be useful when you need to extend it in a subclass, but you can do a better way for this.

+11
source share

The inspect library provides a function for retrieving an attribute without descriptor magic: inspect.getattr_static .

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

(This is an old question, but I come across this all the time when I try to remember how to do it, so I post this answer to find it again!)

+3
source share

Above method

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

A great method is when you manage the property code, but there are some cases, for example, when the property is part of a library controlled by someone else, where a different approach is useful. This alternative approach can also be useful in other situations, such as implementing object matching, walking in the namespace, as described in the question, or in other specialized libraries.

Consider a class with a simple property:

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

When accessed from the dict container object class, the "handle mask" bypasses. Note also that if we assign a property to a new class variable, it will behave the same as the original with โ€œhandle magic,โ€ but if it is assigned to an instance variable, the property behaves like any normal object and also bypasses โ€œhandle magicโ€.

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

Let's say we want a handle to obj.prop where type(obj) is C

C.prop usually works because the descriptor usually returns itself when accessed through C (i.e. attached to C ). But C.prop can call a handle in its metaclass. If prop not in obj , obj.prop will raise an AttributeError , and C.prop not. Therefore, it is better to use inspect.getattr_static(obj, 'prop') .

If this does not suit you, here is a CPython-specific method (from _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') returns the handle in the same way when CPython uses it in obj.prop if obj is a regular object (for example, not a class).

0
source share

All Articles