A hard exercise to help build the right mental model for Python data.

The “Solution” link visualizes execution and reveals what’s actually happening using 𝗺𝗲𝗺𝗼𝗿𝘆_𝗴𝗿𝗮𝗽𝗵: https://github.com/bterwijn/memory_graph

you are viewing a single comment's thread
view the rest of the comments
[–] 20 points 1 month ago (13 children)

The | and |= operators for dict were added in 3.9 which was released in 2020, so it's a relatively recent feature.

I like having an operator for set union but I don't like that a |= b has different semantics to a = a|b. This violates the principle of least surprise, since the former is supposed to be a shorthand for the latter.

  • source
  • hideshow 13 child comments
  • [–] [S] 6 points 1 month ago* (12 children)

    The difference between a |= b and a = a | b is exactly the same as the difference between a += b and a = 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 += b and a = a + b, try with a tuple.

  • source
  • parent
  • hideshow 12 child comments
  • [–] 7 points 1 month ago (11 children)

    I feel that things like this lead people to say something along the lines of ‘mutability was a mistake’

    I'm only half serious, but this is really surprising without Python background

  • source
  • parent
  • hideshow 11 child comments
  • [–] [S] 3 points 1 month ago (10 children)

    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.

  • source
  • parent
  • hideshow 10 child comments
  • [–] 4 points 1 month ago (5 children)

    I don't think mutability is wrong as a concept, albeit I enjoyed learning and toying with Haskell, but I really think these functions in Python should have been two sets of operations, one set to do what a += b does and one set to do what a = a + b does.

    I've been doing C++ for quite some time and amount of implicit things that happen magically and not everyone get them right and this leads to bugs and confusion had really grown on me with time, and I feel like this is the same pattern here, where we get implicit magic instead of being clear with intentions and results

  • source
  • parent
  • hideshow 5 child comments
  • [–] [S] 3 points 1 month ago* (4 children)

    I think the confusion comes from a += b being equivalent to a = a + b for immutable types, so some people generalize that incorrectly to mutable types too. Otherwise I think it's pretty clear a += b mutates a, and a = a + b first computes a + b and then reassigns that to a so that its identity changes, just like in c = 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++.

  • source
  • parent
  • hideshow 4 child comments
  • [–] 1 point 1 month ago (3 children)

    It is only clear if you already have a model of how labels work in Python and while it is understandable to me now, I wouldn't say this is anything I would expect

    Then again, shadowing variables is allowed in other languages and is also a source of confusion and mistakes at times

  • source
  • parent
  • hideshow 3 child comments
  • [–] [S] 1 point 1 month ago (2 children)

    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.

  • source
  • parent
  • hideshow 2 child comments
  • [–] 4 points 1 month ago* (3 children)

    Like anything else, these sorts if issues are rather murky and are directly impacted by the user's competence in the tools.

    Does python have obvious overhead issues? Yes.

    Does Python have to be super inefficient? No.

    Take basic set operations, for example. Do it manually in the language and it will be dog slow. Do it using the set class? You are leveraging the speed of the underlying C implementation.

    E.g., In competitive programming eventually need C or Java, but a strong Python user can move the bar significantly in terms of how many problems are possible with Python.

    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.

    I don't believe this to be the case. The immutability is precisely why efficient structural sharing is possible without screwing up other data structures. And for standard stuff, it is actually happening behind the scenes already.

    You see similar claims about recursion in general, but those claims are often so broad that they don't hold water. I mean, yea, if your language sucks at optimizing recursion it isn't going to be a pleasant experience, but tail call elimination, lazy evaluation, etc. mean you can write some really awesome and efficient code.

    I think the bigger problem is that it takes a lot of time to internalize what is going on under the hood when you make a call or initialize a data structure.

  • source
  • parent
  • hideshow 3 child comments
  • [–] [S] 3 points 1 month ago (2 children)

    I've built a nice visualizer for internalizing what is going on under the hood, I hope that can help people.

  • source
  • parent
  • hideshow 2 child comments