Yes indeed, the 'int' objects are stored and searched in the set based on their value, and the 'Value' objects are stored/searched based on their identity unless you define the above __eq__ and __hash__ methods which cause them to be stored/searched based on value too. This is a Python design decision and that's the point of this exercise. The fact that 'int' is an immutable type and we are replacing isn't relevant. Try replacing/reassigning the 'Value' objects after added __eq__ and __hash__, and you get the same result as for 'int'. So the thing that really matters is how __eq__ and __hash__ are defined, and the default for a user-defined class is as stated in the "Explanation:" above.
Maybe I should also show a class with __eq__ and __hash__ defined based on value, but then it gets a bit long. I'll have to rethink this exercises so that the point comes across better as it now seems to confuse a lot of people based on the down-votes. Thanks for feedback anyway.
Sure, but a lot of people incorrectly think the __eq__ and __hash__ are defined based on value not identity, as they are for many other types say float, str, or tuple. But for a class the default __eq__ method is x is y instead of x == y and also __hash__ is based on identity.
Others assume that if you don't define __hash__ for a class, that it doesn't exists (like for list, set, or dict) so that a "TypeError: unhashable type: 'Value'" exception is raised.
I thought it was an interesting exercise to share, but maybe too simple for this audience, or people are just not aware of the basic steps that happen when adding and searching values in a set/dict. Try the same with type int:
v1 = 1001
v2 = 1002
myset = {v1}
print(v1 in myset, end=' ')
v2 = 1001
print(v2 in myset, end=' ')
v1 = 1002
print(v1 in myset, end=' ')
and you see a different output. To do the same with the Value class add methods:
Copying a list with a million elements every time you make a small change is not fun. Sure, you can optimize a bit behind the scenes, but that still gives a lot of overhead.
It's definitely not the same. Similarly for a class you can define the __add__ dunder method for a + b and separately the __iadd__ dunder method for a += b. The first creates a new object, the latter changes/mutates the existing object a. For immutable types it is the same though.
Yes I understand your point, but I'm trying to reach out so people are aware and can use it in Python education. I feel it can really help beginners understand tricky concepts with ease, bit it's hard to reach a bigger audience these days. Sorry for the repetition, I'll guess I should cut back a bit.
Different languages make different choices. The disadvantage of Haskell is that if you want to change one value in a collection of a million values that it either makes a full copy or tries to optimize by sharing values behind the scene, both resulting in significant overhead. Most people already understand that pure functional programming languages don't deliver except in very specific circumstances: Haskell TIOBE rating 0.32%, https://www.tiobe.com/tiobe-index/
You are right, in landscape mode it's better, but still not ideal. It's a project I don't have time for now. On the other hand, did you run Python code, in an IDE where the debugger visualizes the whole program state, on your Phone before?
I agree that an exercise14.rst would be nice, but to save time I've let the code speak for itself now together with the visualizaion. I'll probably revisit and better document the exercises later.
At the Explanation link I try to give a general explanation about Pyrhon mutability (and copy later on), I agree some readers might find it hard to relate that to a specific exercise, but I don't want to write a specific explanation for each exercise.
Yes indeed, the 'int' objects are stored and searched in the set based on their value, and the 'Value' objects are stored/searched based on their identity unless you define the above
__eq__and__hash__methods which cause them to be stored/searched based on value too. This is a Python design decision and that's the point of this exercise. The fact that 'int' is an immutable type and we are replacing isn't relevant. Try replacing/reassigning the 'Value' objects after added__eq__and__hash__, and you get the same result as for 'int'. So the thing that really matters is how__eq__and__hash__are defined, and the default for a user-defined class is as stated in the "Explanation:" above.Maybe I should also show a class with
__eq__and__hash__defined based on value, but then it gets a bit long. I'll have to rethink this exercises so that the point comes across better as it now seems to confuse a lot of people based on the down-votes. Thanks for feedback anyway.Sure, but a lot of people incorrectly think the
__eq__and__hash__are defined based on value not identity, as they are for many other types say float, str, or tuple. But for a class the default__eq__method isx is yinstead ofx == yand also__hash__is based on identity.Others assume that if you don't define
__hash__for a class, that it doesn't exists (like for list, set, or dict) so that a "TypeError: unhashable type: 'Value'" exception is raised.I thought it was an interesting exercise to share, but maybe too simple for this audience, or people are just not aware of the basic steps that happen when adding and searching values in a set/dict. Try the same with type int:
and you see a different output. To do the same with the
Valueclass add methods:Same thing in Python, double underscore prefix triggers "name mangling". There are issues that can pop up when using it if not aware.
Another common naming convention in C++ is using an underscore as prefix for member variables: int _memberVariable{123};
I like to use this example with my students: https://github.com/bterwijn/memory_graph#lazy-evaluation
Copying a list with a million elements every time you make a small change is not fun. Sure, you can optimize a bit behind the scenes, but that still gives a lot of overhead.
It's definitely not the same. Similarly for a class you can define the
__add__dunder method fora + band separately the__iadd__dunder method fora += b. The first creates a new object, the latter changes/mutates the existing objecta. For immutable types it is the same though.Nice one, see the "Solution" link for correct answer.
Yes I understand your point, but I'm trying to reach out so people are aware and can use it in Python education. I feel it can really help beginners understand tricky concepts with ease, bit it's hard to reach a bigger audience these days. Sorry for the repetition, I'll guess I should cut back a bit.
Different languages make different choices. The disadvantage of Haskell is that if you want to change one value in a collection of a million values that it either makes a full copy or tries to optimize by sharing values behind the scene, both resulting in significant overhead. Most people already understand that pure functional programming languages don't deliver except in very specific circumstances: Haskell TIOBE rating 0.32%, https://www.tiobe.com/tiobe-index/
Incorrect sorry, check the "Solution" link for the correct answer.
Thanks. I didn't know: https://memory-graph.com/#code=x+%3D+%280%2C+%5B1%2C+2%5D%29%0Atry%3A%0A++++x%5B1%5D+%2B%3D+%5B3%5D++%23+calls+list.__iadd__%2C+then+reassigns+x%5B1%5D%0Aexcept+TypeError+as+e%3A%0A++++print%28e%29%0A%0Ax+%2B%3D+%284%2C%29++%23+calls+tuple.__add__%2C+then+reassigns+x%0A%0Aprint%28x%29%0A%0A%0Aclass+MyClass%3A%0A++++%0A++++def+__add__%28self%2C+other%29%3A%0A++++++++print%28%27__add__%27%29%0A++++++++%0A++++%23def+__iadd__%28self%2C+other%29%3A%0A++++%23++++print%28%27__iadd__%27%29%0A%0Aa+%3D+MyClass%28%29%0Ab+%3D+MyClass%28%29%0Aa+%2B%3D+b++%23+calls+__add__+if+__iadd_+doesn%27t+exist%0A&play=
__add__is called with+and__iadd__is called with+=, and there is a difference: https://www.reddit.com/r/PythonLearning/comments/1nw08wu/right_mental_model_for_python_data/Yes
ayou get for free to set the tone, the others are more interesting.Nice one.
The whole point is to practice Python Data Model concepts, it's not a best-way-to-code example, so feel free to hate.
You are right, in landscape mode it's better, but still not ideal. It's a project I don't have time for now. On the other hand, did you run Python code, in an IDE where the debugger visualizes the whole program state, on your Phone before?
C is incorrect,sorry. See the "Solution" link for the correct answer.
Actually running the code? I got to the stage where only AI can help me understand anything ;-)
Thanks for your feedback, much appriciated.
I agree that an
exercise14.rstwould be nice, but to save time I've let the code speak for itself now together with the visualizaion. I'll probably revisit and better document the exercises later.At the Explanation link I try to give a general explanation about Pyrhon mutability (and copy later on), I agree some readers might find it hard to relate that to a specific exercise, but I don't want to write a specific explanation for each exercise.