简体   繁体   中英

Which methods are called when an asterisk/double asterisk is applied to a Python object?

This question was already asked here but doesn't contain any answers.

I'm creating a custom class that inherits from the native Python dict and want to implement some special behavior when an instance of my class is passed to a function with a signature like def foo(*args,**kwargs) .

According to the docs , the relevant methods that have to be overloaded for this are __iter__ , __getitem__ and __len__ , so I setup a basic example to verify what's happening behind the scenes:

class A(dict):
    def __init__(self):
        super(A, self).__init__()

    def __getattribute__(self, item):
        print("__getattribute__ called")
        return super(A, self).__getattribute__(item)

    def __iter__(self):
        print("__iter__ called")
        return super(A, self).__iter__()

    def __getitem__(self, item):
        print("__getitem__ called")
        return super(A, self).__getitem__(item)

    def __len__(self):
        print("__len__ called")
        return super(A, self).__len__()

def foo(*args, **kwargs):
    print("args: ", args)
    print("kwargs: ", kwargs)


a = A()
a['a'] = 1

print("Calling foo() with A")
foo(a)

This just produces the following output:

Calling foo() with A
args:  ({'a': 1},)
kwargs:  {}

It seems that none of the required functions are being called. I overloaded __getattribute__ additionally, just to check which/any class methods/members are being accessed, but nothing seems to be called. What exactly is happening here? I'm using Python 3.7 if that's relevant.

Well, I came across this question today because I was also searching for that answer. I notice though, that nobody has provided an answer and I could not find any answer online as well! Maybe my GoogleFu is not good enough, maybe it is a simple question, or simply nobody has put the effort to answer it.

Here is the answer then!

Let's define two classes.

A first one that inherits from list :

class Foo(list):

    def __init__(self):
        super().__init__()
    
    def __getattribute__(self, item):
        print('__getattribute__ called')
        return super().__getattribute__(item)
    
    def __iter__(self):
        print('__iter__ is called')
        return super().__iter__()
    
    def __getitem__(self, item):
        print('__getitem__ is called')
        return super().__getitem__(item)
    
    def __len__(self):
        print('__len__ is called')
        return super().__len__()

and a second one that inherits from dict:

class Bar(dict):

    def __init__(self):
        super().__init__()
    
    def __getattribute__(self, item):
        print('__getattribute__ called')
        return super().__getattribute__(item)

    def __iter__(self):
        print('__iter__ is called')
        return super().__iter__()

    def __getitem__(self, item):
        print('__getitem__ is called')
        return super().__getitem__(item)
    
    def __len__(self):
        print('__len__ is called')
        return super().__len__()

Then let's define a function to print a dict:

def bar(a, b):
    print(f'a is {a} and b is {b}')

then, let's define our variables:

my_list = Foo()
my_list.extend([1, 2])

my_dict = Bar()
my_dict.update({'a': 1, 'b': 2})

Then, let's see that get's printed!

print(*a)
__iter__ is called
__len__ is called
1 2

and

bar(**b)
__getattribute__ called
__getitem__ is called
__getitem__ is called
a is 1 and b is 2

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM