1

In the section "Invoking Descriptors" of Descriptor HowTo Guide, it says:

" data descriptors always override instance dictionaries.

non-data descriptors may be overridden by instance dictionaries. "

And it also says:

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

Then, the non-data descriptor should always be overridden by instance dictionaries. Why it use "may be" here? How should I understand this statement?

wim
  • 338,267
  • 99
  • 616
  • 750
Spybdai
  • 179
  • 6
  • It says *"may be"* because you might not have anything in the instance dictionary to override them, **not** because if you had something in the instance dictionary to override them they ever *wouldn't* be overridden. – jonrsharpe May 08 '15 at 07:51

1 Answers1

0

Consider the following configuration:

class data_descriptor:
    def __get__(self, obj, cls):
        return 'data descriptor'
    def __set__(self, obj, val):
        pass

class non_data_descriptor:
    def __get__(self, obj, cls):
        return 'non data descriptor'

class X:
    attr_data = data_descriptor()
    attr_non_data = non_data_descriptor()

The data descriptor takes precedence regardless of whether or not you have an item in the instance dictionary. However, the non-data descriptor can be shadowed by an item in the instance dictionary. The docs only write "may be overridden" because it depends on whether or not there is a colliding key present in the instance dictionary.

Here's a demo:

>>> x = X()
>>> x.attr_data
'data descriptor'
>>> x.attr_non_data
'non data descriptor'
>>> # populate the instance dict with stuff
>>> x.__dict__.update({'attr_data': 'spam', 'attr_non_data': 'eggs'})  
>>> x.attr_data  # descriptor wins
'data descriptor'
>>> x.attr_non_data  # instance __dict__ wins
'eggs'
>>> del x.attr_non_data  # restore descriptor by removing name from __dict__
>>> x.attr_non_data
'non data descriptor'

For a discussion of why the descriptor protocol was designed this way, see here.

wim
  • 338,267
  • 99
  • 616
  • 750