6 ms·
It’d be annoying for every type used in a cache to need to properly implement __deepcopy__(), and it’d pose significant performance impact, especially if the ca
by jakear 5y ago
It’d be annoying for every type used in a cache to need to properly implement __deepcopy__(), and it’d pose significant performance impact, especially if the cached objects are large (which there’s a good chance they are, given you’ve felt the need to cache them rather than build them from scratch).
Much better off using the same assignment semantics used throughout Python and let people choose to deepcopy() all the objects they read from the cache if they really really want to modify them later; it would even work as a simple decorator to stack onto the existing one for such cases.
Aside: If we’re missing “deep coping” anything by default in Python, it’s definitely default parameters :) deep copying caches makes much less sense than that.
- nerdponx 5y agoFor anyone wondering what exactly the assignment semantics are, see: https://nedbatchelder.com/text/names1.html https://nedbatchelder.com/text/names1.html and https://www.youtube.com/watch?v=_AEJHKGk9ns https://www.youtube.com/watch?v=_AEJHKGk9ns
- tialaramex 5y ago> An important fact about assignment: assignment never copies data. Is that really what's going on here? I'm in way too deep to be sure what's best for beginner programmers, but I feel like Python must surely optimise... sheep = 1 goats = sheep sheep = sheep + 10 ... by simply copying that 1 into goats, rather than tracking that goats is for now an alias to the same value as sheep and then updating that information when sheep changes on the next line. Now, if we imagine those numbers are way bigger (say 200 digits), Python still just works, whereas the low level languages I spend most time with will object because 200 digit integers don't fit in a machine word. You could imagine that copying isn't cheaper in that case, but I don't think I buy it. The 200 digit integer is a relatively small data structure, still probably cheaper to copy it than mess about with small objects which need garbage collecting.
- hashmush 5y ago> by simply copying that 1 into goats I don't think it does that. You can try it yourself: >>> x = 123456 >>> y = x >>> id(x) 139871830996560 >>> id(y) 139871830996560
- faho 5y ago>Is that really what's going on here? I'm in way too deep to be sure what's best for beginner programmers, but I feel like Python must surely optimise... For these particular numbers, CPython has one optimization I know of: Small integers (from -5 to 256) are pre-initialized and shared. See https://docs.python.org/3/c-api/long.html#c.PyLong_FromLong https://docs.python.org/3/c-api/long.html#c.PyLong_FromLong This is generally invisible to the python programmer, but leads to https://wsvincent.com/python-wat-integer-cache/ https://wsvincent.com/python-wat-integer-cache/
- tialaramex 5y agoThanks! That's a wild ride. On my system these "cached" integers seem to each be 32 byte objects, so, over 8kB of RAM is used by CPython to "cache" the integers -5 through 256 in this way. There's similar craziness over in String town. If I mint the exact same string a dozen times from a constant, those all have the same id, presumably Python has a similar "cache" of such constant strings. But if I assemble the same result string with concatenation, each of the identical strings has a different id (this is in 3.9) So, my model of what's going on in a Python program was completely wrong. But the simple pedagogic model in the article was also wrong, just not in a way that's going to trip up new Python programmers.
- faho 5y ago>presumably Python has a similar "cache" of such constant strings. Not really. You're hitting constant folding: https://arpitbhayani.me/blogs/constant-folding-python https://arpitbhayani.me/blogs/constant-folding-python This isn't a pre-made list of certain strings that should be cached, this is the compiler noticing that you mentioned the same constant a bunch of times. Also in general you would see a lot of things with the same id because python uses references all over the place. E.g. assignment never copies.
- Quekid5 5y agoMight be interesting as an option for the @lru_cache decorator to be able to specify a function to call before handing a cached value to the user. Then you could just do @lru_cache(post=deepcopy, ...) to have a new (copied) instance in cases where that's required. Or do whatever else you needed. Maybe you happen to know some details about the returnee that would let you get away with copying less and could call my_copy instead. Something something power of function composition.