Ankur Agarwal
Ankur Agarwal

Reputation: 24758

Couple of clarifications on python descriptors

Descriptors HowTo

Can someone please help me understand this with an example?:

If an instance’s dictionary has an entry with the same name as a data descriptor, the data descriptor takes precedence. If an instance’s dictionary has an entry with the same name as a non-data descriptor, the dictionary entry takes precedence.

The implementation works through a precedence chain that gives data descriptors priority over instance variables, instance variables priority over non-data descriptors, and assigns lowest priority to _ getattr_() if provided.

Specifically what I don't understand is this:

  1. How can an instance's dictionary have an entry with the same name as the descriptor. Can you please give me an example?

  2. Also can you give me an example where data descriptors are being given priority over instance variables and instance variables priority over non-data descriptors etc?

Also in the code below I was surprised to see self.x in testclass invoking __ set __: I believe this behaviour is because of this explanation in the link above:

For objects, the machinery is in object.__ getattribute __ () which transforms b.x into type(b). __ dict __ ['x']. __ get __ (b, type(b)). Right ??

    #! /usr/bin/env python
    class descclass(object):

            def __init__(self, value):
                    print "in desc init"
                    self.value = value
                    print "out of desc init"                

            def __get__(self, inst, insttype):
                    print "in desc get"
                    return self.value

            def __set__(self,inst, val):
                    print "in desc set"
                    self.value = val

    class testclass(object):

            x = descclass(100)
            print "desc created"

            def __init__(self, val):
                    print "in testclass init"
                    self.x = val  # this invokes __set__ on descriptor in x
                    print "out of testclass init"

    if __name__ == '__main__':

            t = testclass(45)
            print t.x
            print vars(t)
            print vars(testclass)

Upvotes: 1

Views: 171

Answers (2)

Martijn Pieters
Martijn Pieters

Reputation: 1121644

Descriptors are always part of the class; instances have their own attributes and normally they take precedence over any attributes on the class.

Data descriptors are simply descriptors that have both a __get__ and a __set__ method. So Python does this:

  1. See if there is a data descriptor; a descriptor with both hooks.
  2. Look for an attribute on the instance
  3. Look for regular (non-data) descriptor on the class
  4. Fall back to a regular attribute on the class

Point 3. and 4. are really the same thing; descriptors are class attributes with specific methods; so point 4 is really point 3 when there is no descriptor hooks on that attribute.

Upvotes: 1

user2357112
user2357112

Reputation: 280426

Here's an example.

# Not a data descriptor because it doesn't define __set__
class NonDataDescriptor(object):
    def __get__(self, obj, objtype):
        return 3

class Namespace(object):

    # Data descriptor - defines both __get__ and __set__
    @property
    def foo(self):
        return 3

    bar = NonDataDescriptor()

x = Namespace()
x.__dict__['foo'] = 4  # We bypass the foo descriptor's __set__ here,
x.bar = 4              # but there's no bar setter, so this one goes to the dict

# x now has foo and bar descriptors and __dict__ entries

print x.foo  # prints 3 - data descriptor wins over instance __dict__ entry
print x.bar  # prints 4 - instance __dict__ entry wins over non-data descriptor

Upvotes: 4

Related Questions