Karol Zlot
Karol Zlot

Reputation: 4035

__call__ or __init__ called here? Don't undestand which and why

Edit: this is unfortunately not answered in What is the difference between __init__ and __call__ in Python?

class OAuth2Bearer(requests.auth.AuthBase):

    def __init__(self, api_key, access_token):
        self._api_key = api_key
        self._access_token = access_token

    def __call__(self, r):
        r.headers['Api-Key'] = self._api_key
        r.headers['Authorization'] = "Bearer {}".format(self._access_token)
        return r

#############

class AllegroAuthHandler(object):
    def apply_auth(self):
        return OAuth2Bearer(self._api_key, self.access_token)   # what will happen here?

I read about __init__ and __call__, but I still don't undestand what is going on in this code

I don't understand:

1.) Which method will be called, __init__ or __call__

2.) If __init__, then __init__ doesn't return anything

3.) If __call__, then __call__ can't be called with two parameters

I think __init__ should be called, because we have X(), not x() from example below as in this answer:

x = X() # __init__ (constructor)
x() # __call__

Upvotes: 0

Views: 341

Answers (3)

roeen30
roeen30

Reputation: 789

I believe this is what you're looking for.

The behaviour of calling an object in Python is governed by its type's __call__, so this:

OAuth2Bearer(args)

Is actually this:

type(OAuth2Bearer).__call__(OAuth2Bearer, args)

What is the type of OAuth2Bearer, also called its "metaclass"? If not type, the default, then a subclass of type (this is strictly enforced by Python). From the link above:

If we ignore error checking for a minute, then for regular class instantiation this is roughly equivalent to:

def __call__(obj_type, *args, **kwargs):
    obj = obj_type.__new__(*args, **kwargs)
    if obj is not None and issubclass(obj, obj_type):
        obj.__init__(*args, **kwargs)
    return obj

So the result of the call is the result of object.__new__ after passed to object.__init__. object.__new__ basically just allocates space for a new object and is the only way of doing so AFAIK. To call OAuth2Bearer.__call__, you would have to call the instance:

OAuth2Bearer(init_args)(call_args)

Upvotes: 3

rajamohan reddy
rajamohan reddy

Reputation: 111

__init__() is called when used with Class

__call__() is called when used with object of Class

Upvotes: 0

iBug
iBug

Reputation: 37267

I'd say it's neither here.

The part of code that's causing confusion is

OAuth2Bearer(self._api_key, self.access_token)

You need to know one thing: While OAuth2Bearer is the name of a class, it's also an object of class type (a built-in class). So when you write the above line, what's actually called is

type.__call__()

This can be easily verified if you try this code:

print(repr(OAuth2Bearer.__call__))

it will return something like this:

<method-wrapper '__call__' of type object at 0x12345678>

What type.__call__ does and returns is well covered in other questions: It calls OAuth2Bearer.__new__() to create an object, and then initialize that object with obj.__init__(), and returns that object.

You can think of the content of OAuth2Bearer(self._api_key, self.access_token) like this (pseudo-code for illustration purposes)

OAuth2Bearer(self._api_key, self.access_token):
    obj = OAuth2Bearer.__new__(OAuth2Bearer, self._api_key, self.access_token)
    obj.__init__()
    return obj

Upvotes: 0

Related Questions