Python 3 knows the difference between 1
and 1.0
:
>>> type(1)
<class 'int'>
>>> type(1.0)
<class 'float'>
Still, if I try to use both as keys in an ordinary dictionary, the are treated as the same (I'm guessing that is because both hash(1)
and hash(1.0)
is 1
):
>>> {1: 'one', 1.0: 'one point zero'}
{1: 'one point zero'}
What I would like is to have a dictionary-like object my_dict
such that
>>> my_dict
{1: 'one', 1.0: 'one point zero'}
>>> my_dict[1]
'one'
>>> my_dict[1.0]
'one point zero'
Is the some subclass of dict
that is sensitive to the type of each key?
Otherwise, I guess I have to create my own derived class and override the __init__
, __getitem__
, __setitem__
and update
methods? If so, I'm thinking that instead of using key
as a key, I should use (type(key), key)
as a key when accessing the item in the dict
superclass.
Edit: The reason I want such a dictionary is because I have defined the class AutoEnum
:
from enum import IntEnum, auto
class AutoEnum(IntEnum):
def _generate_next_value_(self, _start, count, _last_values):
return count
(I have based it on the Python documentation). Using this class, you can define new enumerations for which the values of the members are automatically assigned, starting at 0, and increasing by 1 for each new member, like so:
class Fruit(AutoEnum):
apple = auto() # 0
pear = auto() # 1
banana = auto() # 2
orange = auto() # 3
class Animal(AutoEnum):
cat = auto() # 0
dog = auto() # 1
I want these to be enumerated like this because I have NumPy arrays in which I use these as indices. I also want a dictionary-like object that can take these as keys and that differentiate between keys that are converted to the same int value but are members of different classes, because sometimes I treat keys from both classes in the same code.
If I try to do the following:
description_dict = {
Fruit.apple: 'A round fruit with a thin peel',
Fruit.pear: 'An oval fruit with a thin peel',
Fruit.banana: 'An oblong fruit with a thick peel',
Fruit.orange: 'A round fruit with a thick peel',
Animal.cat: 'A pet that can accept you under certain circumstances',
Animal.dog: 'A pet that always loves you',
}
the ditionary doesn't turn out the way I intend it to:
>>> for key, val in description_dict.items():
... print("{}: {}".format(repr(key), repr(val)))
...
<Fruit.apple: 0>: 'A pet that can accept you under certain circumstances'
<Fruit.pear: 1>: 'A pet that always loves you'
<Fruit.banana: 2>: 'An oblong fruit with a thick peel'
<Fruit.orange: 3>: 'A round fruit with a thick peel'
CodePudding user response:
As mentioned in the comments, the idea you have doesn't work as is, but an easy and slightly hacky workaround is converting them to strings first, since "1" and "1.0" will be considered distinct keys. The original value can of course be retrieved by converting it back.
CodePudding user response:
This was the solution I chose to use:
class TypedDict(dict):
def __init__(self, data=()):
if isinstance(data, (list, tuple)):
for key, value in data:
self[key] = value
else:
raise NotImplementedError
def __getitem__(self, key):
return dict.__getitem__(self, TypedDict.get_typed_key(key))
def __setitem__(self, key, val):
dict.__setitem__(self, TypedDict.get_typed_key(key), val)
def __repr__(self):
raise NotImplementedError
def update(self, other):
raise NotImplementedError
def keys(self):
raise NotImplementedError
def items(self):
raise NotImplementedError
@staticmethod
def get_typed_key(key):
return type(key), key
Then I can do
>>> my_dict = TypedDict([(1, 'one'), (1.0, 'one point zero')])
>>> my_dict[1]
'one'
>>> my_dict[1.0]
'one point zero'
>>> my_dict[2] = 'two'
>>> my_dict[2.0] = 'two point zero'
>>> my_dict[2]
'two'
>>> my_dict[2.0]
'two point zero'
However, I can't convert class instances to strings for them moment, nor call the keys
, items
and update
methods, because they need to be reimplemented to ignore the types that are stored in the keys.