Is there a Python container that acts like a dictionary but doesn't need both key and value?
Question:
Say I have:
@dataclass
class Foo:
foo_id: int
# Other interesting fields.
def __hash__(self):
return self.foo_id.__hash__()
And I make a foos_set = {Foo(i) for i in range(10)}
. I had always assumed that set.remove
used the hash for constant-time lookup. So I thought it would be reasonable to think that foos_set.remove(6)
should work. But actually, it raises a KeyError
. You’d need to do foo_set.remove(Foo(6))
. In fact, if there are more fields, you need to make sure that all of them match!
I suppose the right thing for me to do is just make a foos_dict = {i: Foo(i) for i in range(10)}
. And I’d be happy to do that, but it just feels unnecessarily clunky so here I am asking if there’s another container I don’t know about.
Answers:
If you want to be able to jump to and delete a particular entry based on an id, use a dictionary
A dictionary is par excellence a container that is indexed by a specific piece of information – the key.
A set is, in effect, indexed on the whole entry, not just the key. A list is indexed on something that is not part of the entry itself, but rather its position in the list.
So
foos_dict = {i: Foo(i) for i in range(10)}
is in fact the perfect way to achieve what you want.
This proves that you need a dictionary!
You are literally describing a dictionary:
I wanted to also be able to retrieve an element from the set using just the ID (without having to do a search).
Say I have:
@dataclass
class Foo:
foo_id: int
# Other interesting fields.
def __hash__(self):
return self.foo_id.__hash__()
And I make a foos_set = {Foo(i) for i in range(10)}
. I had always assumed that set.remove
used the hash for constant-time lookup. So I thought it would be reasonable to think that foos_set.remove(6)
should work. But actually, it raises a KeyError
. You’d need to do foo_set.remove(Foo(6))
. In fact, if there are more fields, you need to make sure that all of them match!
I suppose the right thing for me to do is just make a foos_dict = {i: Foo(i) for i in range(10)}
. And I’d be happy to do that, but it just feels unnecessarily clunky so here I am asking if there’s another container I don’t know about.
If you want to be able to jump to and delete a particular entry based on an id, use a dictionary
A dictionary is par excellence a container that is indexed by a specific piece of information – the key.
A set is, in effect, indexed on the whole entry, not just the key. A list is indexed on something that is not part of the entry itself, but rather its position in the list.
So
foos_dict = {i: Foo(i) for i in range(10)}
is in fact the perfect way to achieve what you want.
This proves that you need a dictionary!
You are literally describing a dictionary:
I wanted to also be able to retrieve an element from the set using just the ID (without having to do a search).