0

I have a class like:

class Cls(object):
     foo = MyItem()
     bar = 'hello'
     def __init__(self, test):
          self.test = test

     def func(self):
         return 'call me'

I want to loop through class members only if they are callable items like foo. In fact MyItem() class implements a __call__ function inside, but it also has attributes like name.

This is MyItem class

class MyItem(object):
    widget = LabelValue()
    name = ""
    data = ""
    def __init__(self, name="", label="", info={}, widget=None):
        if widget is not None:
            self.widget = widget
        self.name = name
        self.label = label
        self.info = info
    def __call__(self, **kwargs):
        self.widget(item =self, **kwargs)

I added this function to my class:

    def __iter__(self):
        for attr in dir(self):
            if not attr.startswith("__") and callable(getattr(self, attr)):
                yield getattr(self, attr)

And I tested it like:

r = {}
for i in Cls():
    r[i] = i

It iterate through MyItem objects, but if I want to access name like

for i in Cls():
    r[i] = i.name

it throws:

    AttributeError: 'function' object has no attribute 'name'

Also if I could somehow have all such members as a list and add to class like _myitems would be good, but I don't know how to do that too.

Ahmad
  • 8,811
  • 11
  • 76
  • 141
  • 1
    The error is because `if not attr.startswith("__") and callable(getattr(self, attr)):` is True for `Cls.foo` (and it works - there is `Cls.foo.name`) and also for `Cls.func` (which raise the error). Maybe use `isinstance()`? That is if you want to get `MyItem.name`. Overall - it is unclear what you want to achieve after all. – buran Jun 17 '22 at 13:17
  • 1
    It's almost always better to create an explicit iterable containing the things you want to iterate over, rather than trying to iterate over the attributes of the class and trying to filter out the things you don't want. – chepner Jun 17 '22 at 13:23
  • @buran `isinstance()` works if it is instance of a subclass of `MyItem()`? In gernal I want to gather all members that are from MyItem class wherever they introduce – Ahmad Jun 17 '22 at 13:50
  • `isinstance()` will work for `MyItem` and its subclasses, `type()` will work for `MyItem` only. Check https://stackoverflow.com/q/1549801/4046632 That said - I am not 100% this is not XY problem, because its not completely clear what you are doing - i.e. there might be better approach – buran Jun 17 '22 at 13:54
  • @buran I can figure out chaning __iter__ with isinstance, but how can I have this collection in another field like `_myitems` so I can aslo explicitly access it – Ahmad Jun 17 '22 at 13:56
  • Sorry, I don't understand what you are talking about – buran Jun 17 '22 at 13:57
  • @buran I mean somthing like `_myitems = [ item for item in dir(self) if .... is instnce of MyItem]`, where can I introduce such attribute? – Ahmad Jun 17 '22 at 13:59
  • @buran I posted an answer to show what I got – Ahmad Jun 17 '22 at 15:00

1 Answers1

0

This can be implemented like the following:

class Cls(object):
     foo = MyItem()
     bar = 'hello'
     def __init__(self, test):
          self.test = test
          self._items = [getattr(self, attr) \
                        for attr in dir(self) \
                        if not attr.startswith("__") \
                        and isinstance(getattr(self, attr), MyItem)]

     def __iter__(self):
        for item in self._items:
            yield item
Ahmad
  • 8,811
  • 11
  • 76
  • 141
  • Your question said you wanted all the callables, but you really only wanted instances of `MyItem` specifically? – ShadowRanger Jun 17 '22 at 15:03
  • @ShadowRanger I mentioned callable attributes like `foo` (only foo) to differentiate it from `func`. However, I am novice to python classes. So, I thought I should be explict about the items I want which are actually an instance of `MyItem`. But if you know any way to differenciate a function from such items, you can offer. – Ahmad Jun 17 '22 at 15:06
  • 1
    Depending on how you want to check it, you'd exclude those with tests for `types.FunctionType` or `types.MethodType`. The former are non-class user-defined functions and functions defined in a class which aren't bound to an instance yet, the latter are functions bound to an instance. – ShadowRanger Jun 17 '22 at 15:44
  • @ShadowRanger Thanks, so that could be a solution too... – Ahmad Jun 17 '22 at 15:47
  • @ShadowRanger, it seems `dir` return fields in alpbabetic order, but I want them unsorted in order of definition, is there an alternative solution to access them? – Ahmad Jun 17 '22 at 18:08
  • @ShadowRanger `vars` and `__dict__` are empty and only `dir` works but I don't want them sorted! – Ahmad Jun 17 '22 at 18:17
  • @Okay I found a trick: https://stackoverflow.com/questions/3288107/how-can-i-get-fields-in-an-original-order – Ahmad Jun 17 '22 at 18:28