Data descriptor doc/implementation inconsistency
Consider this program: class Descr(object): def __init__(self, name): self.name = name def __set__(self, instance, what): instance.__dict__[self.name] = what class X(object): attr = Descr("attr") x = X() print(x.attr) x.attr = 42 print(x.attr) It gives in output: <__main__.Descr object at 0x7fe1c9b28150> 42 The documentation [1] says that Descr is a data descriptor because it defines the __set__ method. It also states that data descriptors always override the value in the instance dictionary. So, the second line should also be the descriptor object according to the documentation. My question is: Is this a doc bug or a implementation bug? If the former, it will be the description of a data descriptor much less consistent, since it will require that a __get__ method be present, too. If the latter, the fix may break some programs relying on the ability to "cache" a value in the instance dictionary. [1] http://docs.python.org/reference/datamodel#invoking-descriptors -- Regards, Benjamin
Hi, 2010/1/11 Benjamin Peterson <benjamin@python.org>:
Consider this program:
class Descr(object): def __init__(self, name): self.name = name def __set__(self, instance, what): instance.__dict__[self.name] = what
class X(object): attr = Descr("attr")
x = X() print(x.attr) x.attr = 42 print(x.attr)
It gives in output:
<__main__.Descr object at 0x7fe1c9b28150> 42
The documentation [1] says that Descr is a data descriptor because it defines the __set__ method. It also states that data descriptors always override the value in the instance dictionary. So, the second line should also be the descriptor object according to the documentation.
My question is: Is this a doc bug or a implementation bug? If the former, it will be the description of a data descriptor much less consistent, since it will require that a __get__ method be present, too. If the latter, the fix may break some programs relying on the ability to "cache" a value in the instance dictionary.
[1] http://docs.python.org/reference/datamodel#invoking-descriptors
Quoting the documentation: """Normally, data descriptors define both __get__() and __set__(), while non-data descriptors have just the __get__() method. """ Your example is neither a data descriptor nor a non-data descriptor... The thing that worries me a bit is the "x.attr" returning the Descr object. Descriptors should remain at the class level, and instance should only see values. I'd prefer an AttributeError in this case. -- Amaury Forgeot d'Arc
2010/1/10 Amaury Forgeot d'Arc <amauryfa@gmail.com>:
Quoting the documentation: """Normally, data descriptors define both __get__() and __set__(), while non-data descriptors have just the __get__() method. """ Your example is neither a data descriptor nor a non-data descriptor...
See the footnote: http://docs.python.org/reference/datamodel#id7
The thing that worries me a bit is the "x.attr" returning the Descr object. Descriptors should remain at the class level, and instance should only see values. I'd prefer an AttributeError in this case.
Far too late for that, I'm afraid. -- Regards, Benjamin
Benjamin Peterson wrote:
My question is: Is this a doc bug or a implementation bug? If the former, it will be the description of a data descriptor much less consistent, since it will require that a __get__ method be present, too. If the latter, the fix may break some programs relying on the ability to "cache" a value in the instance dictionary.
[1] http://docs.python.org/reference/datamodel#invoking-descriptors
I would call it a documentation bug: by leaving out the __get__ you're telling Python to override *just* the setting of the attribute, while still using the normal lookup process for retrieving the attribute (i.e. allowing the attribute to be shadowed in the instance dictionary). Adding a "print('Descriptor Invoked')" to the __set__ method in your example:
x = X() print(x.attr) <__main__.Descr object at 0x7f283b51ac50> x.attr = 42 Descriptor invoked print(x.attr) 42 x.attr = 6*9 Descriptor invoked print(x.attr) 54
Note that the behaviour here is still different from that of a data descriptor: with a data descriptor, once it gets shadowed in the instance dictionary, the descriptor is ignored *completely*. The only way to get the descriptor involved again is to eliminate the shadowing. The non-data descriptor with only __set__ is just choosing not to override the attribute lookup process. Cheers, Nick. -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia ---------------------------------------------------------------
On 11/01/2010 21:12, Nick Coghlan wrote:
Benjamin Peterson wrote:
My question is: Is this a doc bug or a implementation bug? If the
former, it will be the description of a data descriptor much less consistent, since it will require that a __get__ method be present, too. If the latter, the fix may break some programs relying on the ability to "cache" a value in the instance dictionary.
[1] http://docs.python.org/reference/datamodel#invoking-descriptors
[snip...]
Note that the behaviour here is still different from that of a data descriptor: with a data descriptor, once it gets shadowed in the instance dictionary, the descriptor is ignored *completely*. The only way to get the descriptor involved again is to eliminate the shadowing. The non-data descriptor with only __set__ is just choosing not to override the attribute lookup process.
Does that mean we need a third class of descriptors that are neither data descriptors nor non-data descriptors? What should we call them: really-not-data-descriptors? All the best, Michael
Cheers, Nick.
-- http://www.ironpythoninaction.com/ http://www.voidspace.org.uk/blog READ CAREFULLY. By accepting and reading this email you agree, on behalf of your employer, to release me from all obligations and waivers arising from any and all NON-NEGOTIATED agreements, licenses, terms-of-service, shrinkwrap, clickwrap, browsewrap, confidentiality, non-disclosure, non-compete and acceptable use policies (”BOGUS AGREEMENTS”) that I have entered into with your employer, its partners, licensors, agents and assigns, in perpetuity, without prejudice to my ongoing rights and privileges. You further represent that you have the authority to release me from any BOGUS AGREEMENTS on behalf of your employer.
Michael Foord wrote:
Note that the behaviour here is still different from that of a data descriptor: with a data descriptor, once it gets shadowed in the instance dictionary, the descriptor is ignored *completely*. The only way to get the descriptor involved again is to eliminate the shadowing. The non-data descriptor with only __set__ is just choosing not to override the attribute lookup process.
Does that mean we need a third class of descriptors that are neither data descriptors nor non-data descriptors?
Not really - leaving out __get__ when defining __set__ just creates a data descriptor that happens to use the default attribute look-up process rather than defining a different one. (Note that I had the data/non-data terminology backwards in my previous message - I tend to think of the two kinds of descriptor as enforced and non-enforced respectively precisely because I have to think about it to remember that "functions are non-data descriptors and properties are data descriptors, hence non-data descriptors only define __get__ while data descriptors define __set__". I don't find the data/non-data terminology intuitive at all) Cheers, Nick. -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia ---------------------------------------------------------------
participants (4)
-
Amaury Forgeot d'Arc -
Benjamin Peterson -
Michael Foord -
Nick Coghlan