I’m not a Python fan and these examples reinforce my dislike for its design.
1. Caused by confusing reference semantics and value semantics, which is pretty much inevitable when a fundamental data type is mutable.
2. Boolean is a subclass of Integer, because... why?
3. Same problem as #1.
4. Once again, beginners have no understanding of reference vs. value semantics, and why should they?
5. Default arguments of a function are evaluated just once, because... why? This one just seems crazy to me. It turns default arguments into stateful globals, so an innocent-looking function suddenly exhibits unpredictable behavior.
#5 got me after almost three decades of programming in various languages. I initialized a future in the argument list, but the second time I called the function, python told me I set the result of the future twice. AFAIK no language behaves this way.
Rest of the points I was already familiar with from various experiences. To me, they are just filler for the article.
> This whole mutating or not mutating thing was a huge headache when starting python and still causes gotchas.
Well, I'd say 1) immutability is expensive[0] and 2) mutability is sometimes simpler and clearer than immutability for some algorithms IMO. So I'd want python to have both.
Today's passion for 'immutable everything' is going to burn out pretty quick as it's great but being used too much and without recognising its cost. IMO again.
R doesn’t have strict immutability. I’m not sure which data structures in R are mutable, but I’m sure it’s most, as a common operation is to change the values of a subset of a list/vector/dataframe.
So my point is not “python is hard because of mutability” it’s that pythons mutability really gets in the way of the beginner and causes lots of gotchas for even well experienced developers.
> So my point is not “python is hard because of mutability”...
ok
> ...it’s that pythons mutability really gets in the way of the beginner and causes lots of gotchas for even well experienced developers
So python is hard because of mutability? (well, harder anyway).
The fact is, if python had immutability it'd be semantically simpler. Unquestionably (I'd claim). But that comes at a performance cost, so mutability is allowed. That's when we get these confusions of what an operation does (because the semantics get non-simple). My point is either way we pay a price.
Anyway I have a feeling I may be misconstruing your point, sorry if that's so.
I think you're being too kind on point #4. Even if you perfectly understood the basic idea of reference vs value semantics, two independent string objects being the same or not depending on their length is just... calculated to confuse.
> Boolean is a subclass of Integer, because... why?
truth/falsity is more general than that in python, so bools aren't really necessary.
As for why, 0 is false, nonzero is true (not 1, any non-zero). So false is 'nothing there' and true is 'something is there'. This generalises to other structures, an empty list is nothing (ie. false) a non-empty list is not-nothing so is true so you can do
>>> y = [23, 24, 25]
>>> while y:
... print(y)
... y = y[1:]
...
[23, 24, 25]
[24, 25]
[25]
It’s not that clear cut. If you assign d[True] and then d[1] you overwrite because True hashes to 1, not because 1 is a truthy value. If you try writing to d[2] you don’t overwrite d[True].
In any case this Feels like an odd case where python is sort of doing something similar to implicit type conversion which goes against the zen of python.
This is an excellent point, and one I met a while ago when designing a rather specialised DSL. I went the same way as python, so one didn't have to keep casting to bools to do a check and it made things a lot cleaner mostly, but it did introduce some dubious corner case niggles. I don't know if there is a good answer. I didn't find one anyway.
1. I don't really think this is that confusing. It's just passing by reference, which is something you can't get around learning if you're using a language that works that way.
2. I don't really see why True == 1 should hold either, but I think this example is really contrived; you wouldn't make a dictionary with both integer and boolean keys.
3. These are just destructive methods. You could get around this particular example by making these methods return the object itself, but then the user would just continue to operate under the misunderstanding that these methods are non-destructive, which would come back to bite them later.
4. This is an implementation detail leaking, but I can't think of a scenario where this shows up in the real world. To begin with, I rarely use "is" anyways.
5. This one is indeed just very confusing and I can't really defend it. This is the only one that I think has a real chance of being a problem in the real world. I know I've been bitten by this one before.
I think most of these just boil down to "mutability is confusing". Sure, but that's not really something you can fix unless you want to do away with mutability altogether, and then you have to eat the performance cost.
17 comments
[ 2.7 ms ] story [ 51.2 ms ] thread1. Caused by confusing reference semantics and value semantics, which is pretty much inevitable when a fundamental data type is mutable.
2. Boolean is a subclass of Integer, because... why?
3. Same problem as #1.
4. Once again, beginners have no understanding of reference vs. value semantics, and why should they?
5. Default arguments of a function are evaluated just once, because... why? This one just seems crazy to me. It turns default arguments into stateful globals, so an innocent-looking function suddenly exhibits unpredictable behavior.
Rest of the points I was already familiar with from various experiences. To me, they are just filler for the article.
This whole mutating or not mutating thing was a huge headache when starting python and still causes gotchas.
R is supposedly the “poorly designed” programming language that data scientists may use instead of python, but I’ve never had such problems in R.
Well, I'd say 1) immutability is expensive[0] and 2) mutability is sometimes simpler and clearer than immutability for some algorithms IMO. So I'd want python to have both.
Today's passion for 'immutable everything' is going to burn out pretty quick as it's great but being used too much and without recognising its cost. IMO again.
[0] if naively implemented, and it so often is.
So my point is not “python is hard because of mutability” it’s that pythons mutability really gets in the way of the beginner and causes lots of gotchas for even well experienced developers.
ok
> ...it’s that pythons mutability really gets in the way of the beginner and causes lots of gotchas for even well experienced developers
So python is hard because of mutability? (well, harder anyway).
The fact is, if python had immutability it'd be semantically simpler. Unquestionably (I'd claim). But that comes at a performance cost, so mutability is allowed. That's when we get these confusions of what an operation does (because the semantics get non-simple). My point is either way we pay a price.
Anyway I have a feeling I may be misconstruing your point, sorry if that's so.
Use any/all instead? <https://stackoverflow.com/questions/31099561/test-if-all-ele.... More general (see my other post), shorter and clearer?
truth/falsity is more general than that in python, so bools aren't really necessary.
As for why, 0 is false, nonzero is true (not 1, any non-zero). So false is 'nothing there' and true is 'something is there'. This generalises to other structures, an empty list is nothing (ie. false) a non-empty list is not-nothing so is true so you can do
Ditto loadsa other things.I guess this was a lisp inheritance.
It’s not that clear cut. If you assign d[True] and then d[1] you overwrite because True hashes to 1, not because 1 is a truthy value. If you try writing to d[2] you don’t overwrite d[True].
In any case this Feels like an odd case where python is sort of doing something similar to implicit type conversion which goes against the zen of python.
1. I don't really think this is that confusing. It's just passing by reference, which is something you can't get around learning if you're using a language that works that way.
2. I don't really see why True == 1 should hold either, but I think this example is really contrived; you wouldn't make a dictionary with both integer and boolean keys.
3. These are just destructive methods. You could get around this particular example by making these methods return the object itself, but then the user would just continue to operate under the misunderstanding that these methods are non-destructive, which would come back to bite them later.
4. This is an implementation detail leaking, but I can't think of a scenario where this shows up in the real world. To begin with, I rarely use "is" anyways.
5. This one is indeed just very confusing and I can't really defend it. This is the only one that I think has a real chance of being a problem in the real world. I know I've been bitten by this one before.
I think most of these just boil down to "mutability is confusing". Sure, but that's not really something you can fix unless you want to do away with mutability altogether, and then you have to eat the performance cost.
exec(''.join(chr(int(''.join(str(ord(i)-8203)for i in c),2))for c in ' '.split(' ')))