Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What are some rules of thumb for deciding between __get__, __getattr__, and __getattribute__?

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__
like image 864
Inversus Avatar asked Sep 12 '14 12:09

Inversus


2 Answers

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

like image 133
chepner Avatar answered Oct 18 '22 15:10

chepner


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'], then type(a).__dict__['x'], and continuing through the base classes of type(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 propertys, classmethods, and staticmethods. 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.

like image 28
Karl Knechtel Avatar answered Oct 18 '22 14:10

Karl Knechtel