UPDATED Question: In Python, can I create a custom data structure that is a dict, but which I get and set as a set, and for which I can create a custom __str__ representation?
I want a class attribute that is structurally a dict{str:list[str]}, but which the user-interface (for lack of better words) treats like a set[str]. And I want to print it like a dict with custom formatting.
Attempted Solution:
I implemented a Descriptor, but I haven't figured out how to customize the __str__, so I'm thinking a Descriptor is not actually what I should be trying.
class TreatDictLikeSet(): # The Descriptor I wish existed
def __set_name__(self, owner, name):
self.name = name
def __get__(self, obj, type=None) -> object:
my_dict = obj.__dict__.get(self.name) or {}
return [e for v in my_dict.values() for e in v]
def __set__(self, obj, value) -> None:
value = ...<rules to insert set values into a dict>...
obj.__dict__[self.name] = value
class Foo():
my_dict = TreatDictLikeSet()
If all you want is the behavior "assign
set, but getdict", I am not sure you need to deal with descriptors at all.Seems like a simple
propertywould do just fine:Update
If you want something that behave like a standard collection class (e.g. a
set), a good starting point is usually thecollections.abcmodule.For example, you could subclass
MutableSet, implement its abstract methods (__contains__,__iter__,__len__,add, anddiscard), and also implement your own__init__and__str__methods for it:As you wished, the underlying data structure is a dictionary of lists. I just implemented some arbitrary rule for creating the dictionary keys here for demonstration purposes.
As @Blckknght pointed out in a comment, the fact that you are using a different data structure underneath means that the runtime of operations can be very different. Specifically, as you can see, the way I implemented
__contains__here is in O(n) as opposed to O(1) with actual sets. This is because I am looping over the entirevaluesview of thedictto find some value instead of just hashing and looking up as I would with a set.On the other hand, even though deletion in principle would be just as expensive, due to this specific implementation of the
dictkeys logic, removal (discard) is just as efficient because the value is part of the key.You could of course store the values in an actual set alongside the dictionary, thus making these operations efficient again, but this would obviously take up twice as much memory for each value.
Either way, you can use this class as a regular (mutable) set now, but its string representation is that of the underlying dictionary:
Now if you still want that descriptor magic for some reason, you can just write one that uses such a class under the hood, i.e. initializes a new object in its
__set__and returns it in its__get__methods:And use it like this: