Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Neat way to get descriptor object

In Python 3

class A(object):
    attr = SomeDescriptor()
    ...
    def somewhere(self):
        # need to check is type of self.attr is SomeDescriptor()
        desc = self.__class__.__dict__[attr_name]
        return isinstance(desc, SomeDescriptor)

Is there better way to do it? I don't like this self.__class__.__dict__ stuff

like image 989
Antigluk Avatar asked Feb 07 '14 13:02

Antigluk


People also ask

What is a descriptor object?

“Descriptors” are objects that describe some attribute of an object. They are found in the dictionary of type objects.

What is __ get __ in Python?

Python __get__ Magic Method. Python's __get__() magic method defines the dynamic return value when accessing a specific instance and class attribute. It is defined in the attribute's class and not in the class holding the attribute (= the owner class).

What is descriptor method?

A descriptor is a mechanism behind properties, methods, static methods, class methods, and super() . Descriptor protocol : In other programming languages, descriptors are referred to as setter and getter, where public functions are used to Get and Set a private variable.

What is descriptor object in Python?

Descriptors are Python objects that implement a method of the descriptor protocol, which gives you the ability to create objects that have special behavior when they're accessed as attributes of other objects.


1 Answers

A.attr causes Python to call SomeDescriptor().__get__(None, A) so if you have SomeDescriptor.__get__ return self when inst is None, then A.attr will return the descriptor:

class SomeDescriptor():
    def __get__(self, inst, instcls):
        if inst is None:
            # instance attribute accessed on class, return self
            return self

Then you access the descriptor with

desc  = type(self).attr

If the attribute's name is known only as a string, attr_name, then you would use

desc  = getattr(type(self), attr_name)

This works even if self is a instance of a subclass of A, whereas

desc = self.__class__.__dict__[attr_name]

would only work if self is an instance of A.


class SomeDescriptor():
    def __get__(self, inst, instcls):
        if inst is None:
            # instance attribute accessed on class, return self
            return self
        return 4

class A():
    attr = SomeDescriptor()
    def somewhere(self):
        attr_name = 'attr'
        desc  = getattr(type(self), attr_name)
        # desc = self.__class__.__dict__[attr_name]  # b.somewhere() would raise KeyError
        return isinstance(desc, SomeDescriptor)

This shows A.attr returns the descriptor, and a.somewhere() works as expected:

a = A()
print(A.attr)
# <__main__.SomeDescriptor object at 0xb7395fcc>    
print(a.attr)
# 4    
print(a.somewhere())
# True

This shows it works for subclasses of A too. If you uncomment desc = self.__class__.__dict__[attr_name], you'll see b.somewhere() raises a KeyError:

class B(A): pass
b = B()
print(B.attr)
# <__main__.SomeDescriptor object at 0xb7395fcc>   
print(b.attr)
# 4    
print(b.somewhere())
# True

By the way, even if you do not have full control over the definition of SomeDescriptor, you can still wrap it in a descriptor which returns self when inst is None:

def wrapper(Desc):
    class Wrapper(Desc):
        def __get__(self, inst, instcls):
            if inst is None: return self
            return super().__get__(inst, instcls)
    return Wrapper

class A():
    attr = wrapper(SomeDescriptor)()
    def somewhere(self):
        desc  = type(self).attr
        # desc = self.__class__.__dict__[attr_name]  # b.somewhere() would raise KeyError
        return isinstance(desc, SomeDescriptor)

So there is no need to use

desc = self.__class__.__dict__[attr_name]

or

desc = vars(type(self))['attr']

which suffers from the same problem.

like image 51
unutbu Avatar answered Sep 27 '22 19:09

unutbu