4 ms·
In that case what would you expect the following to return? a = [] b = [a]*4 a.append(1) print(b) I mean it is possible to change Python and m
by shiandow 3y ago
In that case what would you expect the following to return?
a = []
b = [a]*4
a.append(1)
print(b)
I mean it is possible to change Python and make [...]*n a shorthand for "repeat this statement n times" but that would be odd on its own.
Lists pass by reference, tuples pass by value, if you want to understand Python you're just going to have to get used to that distinction. There are other options, but most are worse.
- bmitc 3y ago> , if you want to understand Python you're just going to have to get used to that distinction My argument is that that distinction isn't needed, by default, in a high-level language, and that plenty of people get it wrong, understandably. It is simply complexity that is not needed. These simple examples can be toyed with, but sooner or later one is going to want to solve real problems, and unintuitive, default behavior doesn't help with that. Python's mode of operation tends to favor implicit over explicit. And the syntax [...]*n is implicit.
- Skeime 3y agoThe distinction is needed, I think, unless you want to go the purely-functional route. I love that route, but just as many people will complain about purely functional languages being unintuitive. If you want mutable values in your language, you need to expect values to mutate. So what should list multiplication do? - There is the current implementation, where a change to the first entry of [f(x)] * 4 will be reflected in all entries of the list (because they're all references to the same value). - It could only work on list literals. So [f(x)] * 4 would call f four times and collect the values in a list. Then, a = [f(x)]; a * 4 would not work, which would be surprising. - It could work differently on list literals and lists. So [f(x)] * 4 would call f four times collecting the results, but a = [f(x)]; a * 4 would just return a list of four references to the one result of f(x). - It could copy the value in the list. How would you do that if the list contained values like file handlers or network connections that can't be copied? Within the context of Python (an imperative language with mutable values), the current implementation is the most sensible one. If you work in such a language, you must always exercise some care when working with mutable values. Learn your tool! Python could have been designed with different decisions. But who knows if we would even be talking about it today in that case?
- kerkeslager 3y ago> If you want mutable values in your language, you need to expect values to mutate. So what should list multiplication do? bitmc can correct me if I'm misinterpreting them, but I feel like what they're saying is that multiplying a list by a number doesn't make sense, so it should throw a type error. That feels reasonable to me (and it's the choice I'm making in the interpreter I'm writing).
- mypalmike 3y agoIt's really not that complex, but it does have to be learned. Everything in Python is an object with an id. Assignment and parameter passing is by object id. There are some mutable objects ("objects", lists, dicts) and some immutable objects (numbers, strings, tuples). The []*n syntax is treated as assignment, hence the id is duplicated. This isn't an issue with immutable objects since there's no way to change the underlying object - you can only change the id of each object in the list. Obviously there are some gotchas like with mutable objects here where the succinct syntax can be confusing. But I think the simplicity of everything being an id has deep intuitive value and is one reason Python has been so successful, even if many users never really think much about ids.
- kerkeslager 3y ago> Lists pass by reference, tuples pass by value, if you want to understand Python you're just going to have to get used to that distinction. Is that true? I haven't looked at the implementation so I can't say it's impossible, but it would be extremely surprising to me if tuples passed by value, because tuples longer than the size operated upon my individual processor instructions would become expensive to pass around. From a correctness perspective the difference between pass-by-reference and pass-by-value (pass-by-copy) aren't usually important, but for performance it can be a very large difference.
- shiandow 3y agoI can't tell what the underlying implementation is, but either way you can't modify them without creating a new tuple so they behave as if they use value-semantics not reference-semantics. (this is what makes tuples safe as defaults in a method, and lists not) If you compare their ids it does look like python makes multiple instances and passes them around by reference, but without a way to modify them in place it's hard to tell the difference, also Python tries to avoid making unnecessary copies of the same thing if possible. You can try to test it a bit with something like the following: t = (1,2) def f(x): return x is t f(t), f((1,2)), (1,2) is (1,2) # -> True, False, True but the results might be implementation dependent.
- kerkeslager 3y ago> I can't tell what the underlying implementation is, but either way you can't modify them without creating a new tuple so they behave as if they use value-semantics not reference-semantics. (this is what makes tuples safe as defaults in a method, and lists not) Ahh, you were talking about the semantics, not the implementation. Understood. Another way to think of this might be in terms of mutability, i.e. tuples are immutable. Pass-by-value versus pass-by-reference for immutable objects doesn't make much difference in the behavior of objects in a language like Python which doesn't have explicit pointers, but it's a big difference for performance. > If you compare their ids it does look like python makes multiple instances and passes them around by reference I didn't think of answering my own question this way. Nice! > but the results might be implementation dependent. It's possible since Python isn't standardized, but I strongly doubt you'll find a mature implementation that passes potentially-large, variable-sized objects like tuples around by value, for two reasons: 1. Once you get above the size operated on by processor instructions (64 bits on most modern desktop/laptop processors) pass by value requires a memcopy, which is orders of magnitude slower. Tuples can grow past the L1 or L2 cache size, which corresponds to another two orders of magnitude in performnance loss. Comparing this to passing around a 64-bit pointer which is about as fast as passing around any other value. 2. Virtual machines usually implement their own stack. This is easiest to do as an array of same-sized values. Implementing a performant stack with variable-sized elements is a nightmare I wouldn't wish on my worst enemy.