Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why do people default owner parameter to None in __get__?

I've seen this quite often:

def __get__(self, instance, owner=None):

Why do some people use the default value of None for the the owner parameter?

This is even done in the Python docs:

descr.__get__(self, obj, type=None) --> value
like image 782
Markus Meskanen Avatar asked Jul 25 '15 12:07

Markus Meskanen


Video Answer


2 Answers

Because the owner can easily be derived from the instance, the second argument is optional. Only when there is no instance to derive an owner from, is the owner argument needed.

This is described in the proposal that introduced descriptors, PEP 252 - Making Types Look More Like Classes:

__get__: a function callable with one or two arguments that retrieves the attribute value from an object. This is also referred to as a "binding" operation, because it may return a "bound method" object in the case of method descriptors. The first argument, X, is the object from which the attribute must be retrieved or to which it must be bound. When X is None, the optional second argument, T, should be meta-object and the binding operation may return an unbound method restricted to instances of T.

(Bold emphasis mine).

Binding, from day one, was meant to be applicable to the instance alone, with the type being optional. Methods don't need it, for example, since they can be bound to the instance alone:

>>> class Foo: pass
... 
>>> def bar(self): return self
... 
>>> foo = Foo()
>>> foo.bar = bar.__get__(foo)  # look ma! no class!
>>> foo.bar
<bound method Foo.bar of <__main__.Foo object at 0x10a0c2710>>
>>> foo.bar()
<__main__.Foo object at 0x10a0c2710>

Besides, the second argument can easily be derived from the first argument; witness a classmethod still binding to the class even though we did not pass one in:

>>> classmethod(bar).__get__(foo)
<bound method type.bar of <class '__main__.Foo'>>
>>> classmethod(bar).__get__(foo)()
<class '__main__.Foo'>

The only reason the argument is there in the first place is to support binding to class, e.g. when there is no instance to bind to. The class method again; binding to None as the instance won't work, it only works if we actually pass in the class:

>>> classmethod(bar).__get__(None)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: __get__(None, None) is invalid
>>> classmethod(bar).__get__(None, Foo)
<bound method type.bar of <class '__main__.Foo'>>
like image 189
Martijn Pieters Avatar answered Oct 01 '22 20:10

Martijn Pieters


This is the standard way to do it; all Python built-in descriptors I've seen do it, including functions, properties, staticmethods, etc. I know of no case in the descriptor protocol where __get__ will be called without the owner argument, but if you want to call __get__ manually, it can be useful not to have to pass an owner. The owner argument usually doesn't do much.

As an example, you might want a cleaner way to give individual objects new methods. The following decorator cleans up the syntax and lets the methods have access to self:

def method_of(instance):
    def method_adder(function):
        setattr(instance, function.__name__, function.__get__(instance))
        return function
    return method_adder

@method_of(a)
def foo(self, arg1, arg2):
    stuff()

Now a has a foo method. We manually used the __get__ method of the foo function to create a bound method object like any other, except that since this method isn't associated with a class, we didn't pass __get__ a class. Pretty much the only difference is that when you print the method object, you see ?.foo instead of SomeClassName.foo.

like image 30
user2357112 supports Monica Avatar answered Oct 01 '22 21:10

user2357112 supports Monica