Mas Bagol Mas Bagol - 1 month ago 8
Python Question

How to identify if __iter__ is called by dict or list?

What I understand is

__iter__
method makes an object iterable. If it yields a pair, it produces a dictionary if called with
dict
. Now, if I want to create
class that creates a list of values if called with
list
and creates a dict if
called with
dict
, how to do that?

Let say the class is this:

class Container(object):

def __init__(self):
self.pairs = [
('key1', 'val1'),
('key2', 'val2'),
]

def __getitem__(self, idx):
return self.pairs[idx][1] # return value only

def __iter__(self):
for key, val in self.pairs:
yield key, val


Now, if I use
list
or
dict
, I got:

data = Container()
list(data) # [('key1', 'val1'), ('key2', 'val2')]
dict(data) # {'key1': 'val1', 'key2': 'val2'}


What I want (with
list
) is:

list(data) # ['val1', 'val2']


without keys.

Is it possible? If yes, how?

Answer

This is a terrible idea! But you can try reading the calling line to see if the explicit list constructor was calling using the inspect module. Note that this only works when the list constructor is called and will probably produce unexpected results when the regex matches something it wasn't intended to match.

import inspect, re

class Container(object):

    def __init__(self):
        self.pairs = [
            ('key1', 'val1'),
            ('key2', 'val2'),
        ]

    def __getitem__(self, idx):
        return self.pairs[idx][1]  # return value only

    def __iter__(self):
        line = inspect.stack()[1][4][0]
        # print list(Container())

        list_mode = re.match('^.*?list\(.*?\).*$', line) is not None

        for key, val in self.pairs:
            if list_mode:
                yield val
            else:
                yield key, val

print list(Container()) # ['val1', 'val2']