0

I am trying to expose the classes dictionary making it both and subscriptable and be able to iterate through the dict values. Here is the class :

class ExampleClass():
    def __init__(self, *args, **kwargs):
        for key, value in self.kwargs.items():
            setattr(self, key, value)
        for arg in args:
            setattr(self, arg, arg) if isinstance(arg, str) else setattr(self, str(arg), arg)
    def __str__(self):
        return 'This is the example class'
    def __getitem__(self, obj):
        return self.__dict__[obj]
    def __len__(self):
        return len(self.__dict__.items())

If we create an instance and pass in these values :

cls = ExampleClass(123456,'cash', name='newexample', id=1)

This will store all of the args and kwargs as instance attributes, and using the syntax cls['id'] will return 1 as expected. But when I use the syntax for i in cls: print(i) I get a KeyError : KeyError : 0

How can I make this object's dict both subscriptable and iterable ?

1
  • You need to implement the __iter__ function to enable iterability Commented Jul 6, 2019 at 16:28

2 Answers 2

3

You need to implement the __iter__ method.

class ExampleClass():
    def __init__(self, *args, **kwargs):
        for key, value in kwargs.items():
            setattr(self, key, value)
        for arg in args:
            setattr(self, arg, arg) if isinstance(arg, str) else setattr(self, str(arg), arg)
    def __str__(self):
        return 'This is the example class'
    def __getitem__(self, obj):
        return self.__dict__[obj]
    def __len__(self):
        return len(self.__dict__.items())
    def __iter__(self):
        return iter(self.__dict__)

cls = ExampleClass(123456,'cash', name='newexample', id=1)
print(cls['cash'])
print(cls['name'])
for i in cls: print(i)

This is the method which is called to create an iterator for your type so that it can be iterated. Your underlying dict already implements it, so you're sort of just proxying it here.

Sign up to request clarification or add additional context in comments.

5 Comments

We should note as well that you code fixes the self.kwargs bug in the question
Thanks @paulprescod I thought I would have needed both the __iter__ and the __next__ predefined variables to make this work, but wasn't sure how!
Just for clarity, you don't actually need to implement __iter__. It would be enough if your __getitem__ could accept integers starting at 0 through the length of your instance.
Also @Paul the solution will print the keys, but how do I print the values ?
@jakuta: Do you want the code that USES the objects to print the values? If so then you just need to look at for i in cls: print(i, cls[i]) Or do you want the object itself to always return key/value pairs? If so, I'd suggest you implement an items() method to emulate the way dictionaries in Python work. __iter__ could also return tuples but you might as well copy how dictionaries work IMO. If you do need the __iter__ to return tuples then the body of it would look more like return iter(self.__dict__.items())
0

To make a class subscriptable, it must contain dunder getitem(), it may or may not contain dunder iter().

At the same time, an iterable must contain iter().

To check if your class has the required method, perform print(dir(your_class)), and look for the respective dunder function.

If you don't have one, create it.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.