The point of the visualization at the Solution link is precisely to help people get the right model of how labels and the data model in general works in Python. See the Explanation link for more details.
- 19 Posts
- 45 Comments
Great, I hope it can bring much value for your teaching.
If you are interested see the dictionary docs (https://docs.python.org/3/builtins/stdtypes.html#dict):
d |= otherUpdate the dictionarydwith keys and values fromother, which may be either a mapping or an iterable of key/value pairs. The values ofothertake priority whendandothershare keys. Added in version 3.9.Pick your answer and check the Solution link.
There is one important difference between
a |= banda = a | b, that is one part that makes this exercise difficult. See the dictionary docs (https://docs.python.org/3/builtins/stdtypes.html#dict):d |= otherUpdate the dictionarydwith keys and values fromother, which may be either a mapping or an iterable of key/value pairs. The values ofothertake priority whendandothershare keys. Added in version 3.9.Pick your answer and check the Solution link.
The zen of Python lies unless you’re Dutch. I’m Dutch and when I look out of my office I see the CWI where Guido wrote the original Python versions back in the day, see “Python the documentory” on YouTube.
I’ve built a nice visualizer for internalizing what is going on under the hood, I hope that can help people.
I think the confusion comes from
a += bbeing equivalent toa = a + bfor immutable types, so some people generalize that incorrectly to mutable types too. Otherwise I think it’s pretty cleara += bmutatesa, anda = a + bfirst computesa + band then reassigns that toaso that its identity changes, just like inc = a + b.If you implement these operations in a class you have to implement each dunder,
__iadd__(self, other)and__add__(self, other)separately, same thing in C++.
Thanks, maybe I should have added that possible answer instead of ‘D’, adding one more feels too many.
If you don’t want mutability you have to go to a pure functional language like Haskell, but then you have to copy a big list every time you make a change. There are ways to optimize copying by secretly sharing data behind the scene but you pay a performance price in some way. Then again, Python is slow and has mutability but popular for other reasons.
Thanks, I’m pretty happy with it myself.
The difference between
a |= banda = a | bis exactly the same as the difference betweena += banda = a + b. Nothing new about that, try:a = [1] b = a b += [2] b.append(3) b = b + [4] b.append(5) print(a)or see: https://fosstodon.org/@bterwijn/116328991093782469
For immutable types there is no difference between
a += banda = a + b, try with a tuple.
Diagnostics came up with some “double percent-encoding” issue on IOS. I’ve updated the website with a workaround, hopefully it does work now. Thanks for bug reporting.
This is what the solution link is supposed to give you at the end of the execution animation:

It’s hard to support all mobile browsers, on some it does work.
It’s mainly targeted to Python programmers at: https://programming.dev/c/python
The Solution link gives you a visualization of program execution, I’ve started to make the same sort of visualizer for C. What do you think?
Can you tell me what the problem is for you when you click the first link? I tried to make it work for all browsers (even mobile, but the UI isn’t very mobile friendly), so I would like to get it working on your system too. If you can give me a bug report (and maybe try from different system), that would be great.
The second link explains:
- mutability: https://github.com/bterwijn/memory_graph#mutable-type
- shallow copy: https://github.com/bterwijn/memory_graph#copying-values-of-mutable-type
- difference between
x += yandx = x + y: https://github.com/bterwijn/memory_graph#name-rebinding
It’s left to the reader to generalize this to
x |= yvsx = x | y.
The dict union operator is similar to that of set, but in addition the right operand overwrites values of keys of the left operand. It’s a nice operator once you get used to it.
Easy to make a mistake with this exercise:
- first realize
b = b | {3: []}makes a shallow copy - then realize the shallow copy still references the value of key
2inaso we can still append to that and changea
- first realize
bterwijn@programming.devOPto
Python@programming.dev•Adding objects to set or dictionary: equality and hashing
1·1 month agoYes 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.
bterwijn@programming.devOPto
Python@programming.dev•Adding objects to set or dictionary: equality and hashing
31·1 month agoSure, 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:
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
Valueclass add methods:def __eq__(self, other): return self.value == other.value def __hash__(self): return hash(self.value)
bterwijn@programming.devOPto
Python@programming.dev•Difference between 'instance', 'class', and 'static' method visually explained
1·3 months agoSame thing in Python, double underscore prefix triggers “name mangling”. There are issues that can pop up when using it if not aware.
bterwijn@programming.devOPto
Python@programming.dev•Difference between 'instance', 'class', and 'static' method visually explained
11·3 months agoAnother common naming convention in C++ is using an underscore as prefix for member variables: int _memberVariable{123};
Thanks a lot, I hope it can bring much value for you.