What are some general rules of thumb for choosing which of these to implement in a given class, in a given situation?
I have read the docs, and so understand the difference between them. Rather, I am looking for guidance on how to best integrate their usage into my workflow by being better able to notice more subtle opportunities to use them, and which to use when. That kind of thing. The methods in question are (to my knowledge):
## fallback
__getattr__
__setattr__
__delattr__
## full control
__getattribute__
##(no __setattribute__ ? What's the deal there?)
## (the descriptor protocol)
__get__
__set__
__delete__
__setattribute__
does not exist because __setattr__
is always called. __getattr__
is only called for f.x
if the attribute lookup fails via the normal channel (which is provided by __getattribute__
, so that function is similarly always called).
The descriptor protocol is slightly orthogonal to the others. Given
class Foo(object):
def __init__(self):
self.x = 5
f = Foo()
The following are true:
f.x
would invoke f.__getattribute__('x')
if it were defined.f.x
would not invoke f.__getattr__('x')
if it were defined.f.y
would invoke f.__getattr__('y')
if it were defined, or else
f.__getattribute__('y')
if it were defined.The descriptor is invoked by an attribute, rather than for an attribute. That is:
class MyDescriptor(object):
def __get__(...):
pass
def __set__(...):
pass
class Foo(object):
x = MyDescriptor()
f = Foo()
Now, f.x
would cause type(f).__dict__['x'].__get__
to be called, and f.x = 3
would call type(f).__dict__['x'].__set__(3)
.
That is, Foo.__getattr__
and Foo.__getattribute__
would be used to find what f.x
references; once you have that, f.x
produces the result of type(f.x).__get__()
if defined, and f.x = y
invokes f.x.__set__(y)
if defined.
(The above calls to __get__
and __set__
are only approximately correct, since I've left out the details of what arguments __get__
and __set__
actually receive, but this should be enough to explain the difference between __get__
and __getattr[ibute]__
.)
Put yet another way, if MyDescriptor
did not define __get__
, then f.x
would simply return the instance of MyDescriptor
.
For __getattr__
vs __getattribute__
, see for example Difference between __getattr__ vs __getattribute__ .
__get__
is not really related. I'm going to quote from the official documentation for the descriptor protocol here:
The default behavior for attribute access is to get, set, or delete the attribute from an object’s dictionary. For instance, a.x has a lookup chain starting with
a.__dict__['x']
, thentype(a).__dict__['x']
, and continuing through the base classes oftype(a)
excluding metaclasses. If the looked-up value is an object defining one of the descriptor methods, then Python may override the default behavior and invoke the descriptor method instead. Where this occurs in the precedence chain depends on which descriptor methods were defined.
The purpose of __get__
is to control what happens once a.x
is found through that "lookup chain" (for example, to create a method object instead of returning a plain function found via type(a).__dict__['x']
); the purpose of __getattr__
and __getattribute__
is to alter the lookup chain itself.
There is no __setattribute__
because there is only one way to actually set an attribute of an object, under the hood. You might want to cause other things to happen "magically" when an attribute is set; but __setattr__
covers that. __setattribute__
couldn't possibly provide any functionality that __setattr__
doesn't already.
However - the best answer, in the overwhelming majority of cases, is to not even think of using any of these. First look to higher-level abstractions, such as property
s, classmethod
s, and staticmethod
s. If you think you need specialized tools like this, and can't figure it out for yourself, there's a pretty good chance you're wrong in your thinking; but regardless, it's better to post a more specific question in that case.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With