Creating a set with dataclass and dict - Printable Version +- Python Forum (https://python-forum.io) +-- Forum: Python Coding (https://python-forum.io/forum-7.html) +--- Forum: General Coding Help (https://python-forum.io/forum-8.html) +--- Thread: Creating a set with dataclass and dict (/thread-23753.html) Pages:
1
2
|
Creating a set with dataclass and dict - hobbitdur - Jan-15-2020 Hi everyone. I am trying to play with the new @dataclass and TypedDict from 3.7 and 3.8 But after searching and reading lots of documentation I cannot find a proper answer on this. I think the answer is simple but it's not clear to me. Here the snippet: from dataclasses import dataclass @dataclass(frozen=True, eq=True, repr=False) class Building: name: str property: dict foo = Building(name="Test", property={"key": 10}) var = {foo}and the error: File "<string>", line 3, in __hash__ TypeError: unhashable type: 'dict'Ok the message is clear, but I don't understand why all nested element of a set need to be hashable. I don't find anything that tell me: a set take only hashable element that have itself hashable element Obviously if I replace the property with a hashable object it works. Thank you for your help Hobbit RE: Creating a set with dataclass and dict - micseydel - Jan-15-2020 I'm disappointed that I didn't find that information at https://docs.python.org/3/tutorial/datastructures.html#sets They do mention it for dictionaries, but still not as clearly as I would have liked. The easiest workaround for you is to use a frozenset instead of a set. If that doesn't solve your problem please describe the goal a bit more clearly. RE: Creating a set with dataclass and dict - micseydel - Jan-15-2020 Ok, so I still think the official Python tutorial should have that info, but the reference docs do say: https://docs.python.org/3/library/stdtypes.html#set-types-set-frozenset Wrote:A set object is an unordered collection of distinct hashable objects.Mutable data structures (such as sets) are not hashable, so they can't be in sets or used as dict keys. RE: Creating a set with dataclass and dict - hobbitdur - Jan-16-2020 micseydel, thank you for your kind help. But I am not sure I understood your answer. You speak about using set in set or dict key. That's not what I meant. My problem is the line 11 of my code. I have a dataclass that is Hashable (frozen and eq to True), but I cannot use it as an element of my set. import collections from dataclasses import dataclass @dataclass(frozen=True, eq=True, repr=False) class Building: name: str property: dict foo = Building(name="Test", property={"key": 10}) print(isinstance(foo, collections.Hashable)) var = {foo} Thank you again !
RE: Creating a set with dataclass and dict - buran - Jan-16-2020 I think there is misunderstanding - frozen means that you cannot assign to fields, but in your case the property field is mutable type dict - i.e. you can change it without assignment (i.e. frozen argument is irrelevant in this case)from dataclasses import dataclass # frozen dataclass with mutable field type @dataclass(frozen=True, eq=True, repr=False) class Building: name: str properties: dict foo = Building(name="Test", properties={"key": 10}) foo.properties['key2'] = 20 print(foo.properties) # dataclass, not frozen, assignment to field possible @dataclass(eq=True, repr=False) class Building: name: str count: int foo = Building(name="Test", count=1) foo.count = 2 print(foo.count) # frozen dataclass, assignment to filed not possible @dataclass(frozen=True, eq=True, repr=False) class Building: name: str count: int foo = Building(name="Test", count=1) foo.count = 2
depending on what your ultimate goal is, you may use namedtuple from dataclasses import dataclass from collections import namedtuple properties = namedtuple('Properties', 'key key2') # frozen dataclass with namedtuple field type @dataclass(frozen=True, eq=True, repr=False) class Building: name: str properties: namedtuple foo = Building(name="Test", properties=properties(key=10, key2=20)) print(foo.properties) print(foo.properties.key2) var = {foo} print(var, type(var))
RE: Creating a set with dataclass and dict - hobbitdur - Jan-16-2020 Thank you for the explanation ! You helped me a lot. I agree NamedTuple is useful here. But what I don't understand is the following sentence from the doc: A set object is an unordered collection of distinct hashable objects. And my class Building is Hashable. So why it needs to me immutable in order to be used in a set ? I don't find a clear reference.So here the result that I have done and that is working. I will explain my aim in an other post as it would be different that the title of this post. My question of this post was only about why I cannot use a hashable object in a set. from dataclasses import dataclass @dataclass(frozen=True, eq=True) class BuildingProperty: stone: int wood: int architecture: int decoration: int victory_point: int money: int @dataclass(frozen=True, eq=True) class Building: name: str properties: BuildingProperty foo = Building(name="Test", properties=BuildingProperty(stone=2, wood=1, architecture=0, decoration=1, victory_point=8, money=4)) var = {foo} RE: Creating a set with dataclass and dict - buran - Jan-16-2020 (Jan-16-2020, 09:42 AM)hobbitdur Wrote: And my class Building is Hashable.No, it's not hashable*, because one of the fields is dict, which is mutable and not hashable, thus the whole Building dataclass is not hashable*. as it says in the docs: Quote:hashable EDIT, based on the discussion below, to avoid confusion: * hashable in the sense that when you pass object to hash() function it will not raise error, not that it is instance of collections.abc.Hashable and provide __hash__() method RE: Creating a set with dataclass and dict - perfringo - Jan-16-2020 EDIT: ninjad by buran. I think there are some subtleties. Not all objects which are considered as immutable are hashable. Python glossary: immutable: Quote:An object with a fixed value. Immutable objects include numbers, strings and tuples. Such an object cannot be altered. A new object has to be created if a different value has to be stored. They play an important role in places where a constant hash value is needed, for example as a key in a dictionary. Objects, values and types Quote:The value of some objects can change. Objects whose value can change are said to be mutable; objects whose value is unchangeable once they are created are called immutable. (The value of an immutable container object that contains a reference to a mutable object can change when the latter’s value is changed; however the container is still considered immutable, because the collection of objects it contains cannot be changed. So, immutability is not strictly the same as having an unchangeable value, it is more subtle.) An object’s mutability is determined by its type; for instance, numbers, strings and tuples are immutable, while dictionaries and lists are mutable. RE: Creating a set with dataclass and dict - hobbitdur - Jan-16-2020 (Jan-16-2020, 09:49 AM)buran Wrote:(Jan-16-2020, 09:42 AM)hobbitdur Wrote: And my class Building is Hashable.No, it's not hashable, because one of the fields is dict, which is mutable and not hashable, thus the whole Building dataclass is not hashable. Thank you. Ok, then why the line print(isinstance(foo, collections.Hashable)) return True ?
RE: Creating a set with dataclass and dict - buran - Jan-16-2020 as stated in the docs collections.abc.Hashable are classes that provide __hash__() method. i.e. it doesn't mean it's guaranteed that when you call that method it will not raise error.Not exactly the same, but it similar - if you look at data model docs for object.__hash__() Quote:A class which defines its own __hash__() that explicitly raises a TypeError would be incorrectly identified as hashable by an isinstance(obj, collections.abc.Hashable) call.Your Buildings has __hash__ method but when called it raises TypeError. dataclasses.dataclass [may] provides __hash__() - check unsafe_hash attribute for details. In your case it will be forced to create __hash__ method because both frozen and eq are set True. However when you it is called when creating the set you get the TypeError. You will get same error if you try to call hash(some_dict) .Note that this is not unique to your case., e.g. import collections.abc foo = ([1, 2], 'a') print(type(foo)) print(isinstance(foo, collections.abc.Hashable)) hash(foo) Bottom line - be careful with nested data structures.
|