0

I will start right away with code. I have two classes

class Struct:
    def __init__(self, **entries):
        self.__dict__.update(entries)
        self.__keys__ = list(entries.keys())

    def __iter__(self):
        for i in self.__keys__:
            yield self.__dict__.get(i)

    def __len__(self):
        return len(self.__keys__)

    def __getitem__(self, item):
        if item in self.__keys__:
            return self.__dict__.get(item)
        else:
            return None

    def add(self, **entries):
        for key, value in entries.items():
            if key not in self.__keys__:
                self.__keys__.append(key)
            self.__dict__[key] = value

    @staticmethod
    def from_struct(struct: 'Struct'):
        return Struct(**struct.dict())

    def dict(self):
        return {key: self.__dict__.get(key) for key in self.__keys__}

class StructList:

    def __init__(self, structlist: list[Struct] = []):
        self.structlist = structlist

    def append(self, struct: Struct):
        self.structlist.append(struct)

    def __iter__(self):
        for struct in self.structlist:
            yield struct

This is basic classes for Struct and StructList. This classes are used for generating some child-classes, but I found some problem, which I didn't understand yet. If I generate some Struct objects, they are working pretty well:

x = Functional.Struct(a="1", b=2)
y = Functional.Struct.from_struct(x)
y.a = 12
y.add(c=12)
y.add(**{"d": 14})
z = Functional.Struct(**{"c":"1", "d":2})

print(x)
print(x.dict())
print(y)
print(z)

They are different, and that's good. But, when I try to generate StructList, there is some problems:

structlist1 = Functional.StructList()
structlist1.append(x)
structlist1.append(y)

for struct in structlist1:
    print(struct)

print()

structlist2 = Functional.StructList()
    
for struct in structlist2:
    print(struct)

Problem is - when I create first StructList, it generates perfectly. Then I add here some Struct objects inside via method append, which I created something like presented above. This objects correctly holds inside this StructList. But, when I create second StructList, and trying to check what inside, I found, that this isn't empty. It contains all Struct objects from the first StructList, but I haven't linked on it, I just created new instance of this class. I think it should be empty, and doesn't linking with created ones. So, question is - how to fix this kind of problems? I don't want to use default list class to make this StructList child class, but want to use this, created one.

In additional, these objects has different adressess:

print(structlist1)
print(structlist2)
print(structlist1 == structlist2)

gives

<Scripts.Generators.Functional.StructList object at 0x7f12cb650700>
<Scripts.Generators.Functional.StructList object at 0x7f12cb650280>
False

After updating Struct class from comments, there isn't solved this problem:

class Struct:

    def __init__(self, **entries):
        self.__dict__.update(entries)

    def __iter__(self):
        return iter(vars(self).values())

    def __len__(self):
        return len(self.__keys__)

    def __getitem__(self, item):
        return self.__dict__.get(item)

    def add(self, **entries):
        for key, value in entries.items():
            self.__dict__[key] = value

    @staticmethod
    def from_struct(struct: 'Struct'):
        return Struct(**struct.dict())

    def dict(self):
        return dict(vars(self))
  • 1
    Default arguments are evaluated once at function definition time – juanpa.arrivillaga Jul 19 '21 at 16:01
  • As an aside, what is the point of `self.__keys__ = list(entries.keys())`? Note, you shouldn't be making up your own dunder attributes – juanpa.arrivillaga Jul 19 '21 at 16:03
  • This argument used to be for available keys in this struct. I mean that there is no other keys will be used when iterate this object, and when iterate, it will be in specific order. – Arios Jentu Jul 19 '21 at 16:05
  • It is unnecessary. Just use `vars(self).keys()`, and for `__iter__`, for example, all you need is `return iter(vars(self).values())`, or for another example, `def dict(self)` should just be `return dict(vars(self))` – juanpa.arrivillaga Jul 19 '21 at 16:08
  • An `__getitem__` should just be `return getattr(self, item, None)`. note, even if you use your original approach, checking if `item in __keys__: ... else: return None` is pointless, you could have just done `return self.__dict__.get(item)` which will return `None` by default. the normal way to access an item in a dictionary is to use `self.__dict__[item]` – juanpa.arrivillaga Jul 19 '21 at 16:12
  • I've tested this example, and there isn't solved the main problem of this question – Arios Jentu Jul 19 '21 at 16:25
  • What did you *test*? My comments about `__keys__` aren't about your issue, those were an *aside*. Did you look at the linked duplicate? Don't use a default argument, `structlist = []`. because then every time you use the default argument it uses *that same list object*. Again, **read the linked duplicate** – juanpa.arrivillaga Jul 19 '21 at 16:25
  • @juanpa.arrivillaga about default arguments, this is interesting. Need to create default new instance of empty list, I think, because default argument is should be for this class and it's child-classes – Arios Jentu Jul 19 '21 at 16:28
  • 1
    **read the duplicate**. This is a fundamental issue with functions in Python, the fact that a class is involved is irrelevant. Just use `None` as a default then in the body `if structlist is None: structlist = []` – juanpa.arrivillaga Jul 19 '21 at 16:34
  • Ye, didn't know well about problems with lists as default arguments. For this class I've used solution in form of creating a copy of list from argument: `def __init__(self, structlist: list[Struct] = []): self.structlist = structlist.copy()` – Arios Jentu Jul 19 '21 at 16:34
  • Thank you for this discussion. – Arios Jentu Jul 19 '21 at 16:36
  • 1
    The behavior applies to all default arguments, but it only matters for mutable types – juanpa.arrivillaga Jul 19 '21 at 16:40

0 Answers0