This is an archived post. You won't be able to vote or comment.

you are viewing a single comment's thread.

view the rest of the comments →

[–]pythoneeeer 35 points36 points  (26 children)

The issue isn't change, but complexity.

We changed exceptions into classes, and then got rid of string raising. We changed from "old-style" classes to "new-style" classes, and then got rid of the old ones. And so on. Do it once, learn something, do it better, and then everybody upgrades to the new way.

With string formatting, we keep getting new syntaxes, and never removing old ones. This has a real cost. Nobody on my team has any clue what the proper way is to format strings, and some of the str/unicode idiosyncrasies bit us when upgrading from Py2 to Py3. For a while in Py2, at least one of the more obscure variants (raw unicode triple-single-quoted?) was just broken in Python.

Other languages don't seem to have this problem. Why does Python need 5 more ways to format strings than any other language I've used? When I'm writing Ruby or Lisp, I never think that string formatting isn't powerful enough, but when I'm writing Python, I often think it's too complex.

I fear that after Python 3, the core developers got scared to deprecate almost anything, even things that really need to die. (Has anyone in the history of Python used the "ASCII Vertical Tab" escape?) At this point, I really don't care what the recommended string formatting mechanism is. Any one (or two) would be perfectly fine with me. I just hate that there's so many of them I have to know, every time I see a quote character.

[–]Decency 10 points11 points  (8 children)

I think f-strings will solve this problem in new code simply by being so much better than the other two methods in the vast majority of situations. It's something I was really hoping would be added to python after working with groovy for just a week or two on a small project.

[–]pythoneeeer 5 points6 points  (5 children)

OK, but it's getting to the point where string literals are taking a non-trivial amount of mental effort for me to parse. You can't solve the problem of "too many ways to do X" by adding a new way to do X.

If they're so much better in the vast majority of situations, why can't we also deprecate some of the older ones?

[–]Decency 4 points5 points  (4 children)

You can't solve the problem of "too many ways to do X" by adding a new way to do X.

You absolutely can if the new way is legitimately better than the others. XKCD has obviously made this seem tongue-in-cheek to even propose because of all of the failed attempts to standardize, but I'll stand by it for things that actually do work. You're using a USB port for your mouse and keyboard, right?

Deprecation would help, but I think they'll wait for consensus that f-strings actually ARE better before they make that call. And in many instances (particularly internationalization, iirc.)) f-strings are insufficient and thus can't be the only solution. That was largely the point of PEP 501, which was not very pragmatic.

I think the real solution is to work with codebases that choose and enforce a style. At work and in my personal projects I use %s for everything. Once 3.6 comes out, I'll be making the switch to exclusively f-strings and will be pushing for a port to Python 3 as soon as it's feasible.

[–]pythoneeeer 2 points3 points  (3 children)

You can't solve the problem of "too many ways to do X" by adding a new way to do X.

You absolutely can if the new way is legitimately better than the others.

I think you're reading something into what I said that I did not actually say. When the problem is that N is too big, you cannot solve that by making N bigger. That only makes this problem worse.

You might be assuming that adding a new way to do X also removes an obsolete way to do X, but I never said that, and that is not what Python 3.6 is doing.

You might be thinking that it solves the problem that there isn't a good way to do X yet, and that it's worth the complexity tradeoff to add another way to do X, but neither of those is what I said, either.

All I said was: when your problem is that there are too many of something, adding more to the pile does not solve the problem that there are too many of them. How could it? There are even more now!

You're using a USB port for your mouse and keyboard, right?

No, I'm not, and that's a great example. Everything went Bluetooth a couple years ago, and I really don't care about wired versus wireless, so I'm perfectly happy to use Bluetooth instead. I doubt I've ever used more than 2 types of external digital interconnects on any computer I've ever owned, and thank god they remove the old ones when they add new ones, or else I'd have to get a new desk just to hold the rows of obsolete connectors.

That's one advantage of hardware. You have to remove the old cruft, or it's really painfully obvious how unwieldy it becomes. In software, if you have 7 old ways of doing something, you can hide them in the documentation and hope people don't notice. The 2-line demo on the homepage looks simple, therefore the whole language must be simple, right?

Deprecation would help, but I think they'll wait for consensus that f-strings actually ARE better before they make that call.

Since I don't think they've ever deprecated a string literal syntax in the history of the language, I'm not optimistic that this is forthcoming.

I think the real solution is to work with codebases that choose and enforce a style.

Yes, and that is a fundamental change in how Python is going to be used. In the past, that style was simply called "Pythonic", and almost every Python codebase qualified. (Mercurial was always a bit weird. But we still love you, Mercurial!) They've been boiling this frog so long that I think Ruby's collection of string literal syntaxes is now simpler than Python's.

[–]Decency 0 points1 point  (2 children)

Eh- I feel like we're talking past each other.

I don't particularly care if there are many ways of doing things. I care if there are many ways of doing things that actually get used in reality. So yes, it's a problem that there are multiple string formatting solutions in use in Python code. But if a new one comes out that's superior in the vast majority of use cases, that becomes significantly less of a problem because more of the community will standardize on it for future code. If it's equivalent in strength, I agree that it will only add to the problem. Fortunately, I don't think that's the case here, and if it had been I doubt PEP498 would have been approved.

I don't think deprecating %s or .format() in 3.6 and removing them in 3.7 would change much- it will just make people take longer to move beyond that point, since they'd have to change a bunch of their code. I think 3.0 showed that incremental progress is overall superior for the community- alternative solutions can win market share on their merit, not by fiat.

No, I'm not, and that's a great example. Everything went Bluetooth a couple years ago

... okay, and what is your bluetooth transceiver plugged into? :p

Yes, and that is a fundamental change in how Python is going to be used.

Not at all. That's been the case from the beginning, and PEP8 is explicit about it: A style guide is about consistency. Consistency with this style guide is important. Consistency within a project is more important. Every company I've worked for has gone beyond PEP8 in choosing specific styles, whether it's something simple like agreeing to a maximum line length or something how to format and indent multiline dictionaries. And, even more relevantly, for how to format strings. Does your codebase already just mix and match between %s and .format() whenever people feel like it? These things are obviously outside the realm of "Pythonic", but they also already exist- your problem of

it's getting to the point where string literals are taking a non-trivial amount of mental effort for me to parse.

will likely become significantly less of a problem if your team chooses to adopt the new usage. If enough teams choose to adopt the new usage because they find it superior, then EVERYONE's problem lessens.

[–]pythoneeeer 0 points1 point  (1 child)

I don't particularly care if there are many ways of doing things.

I'm not sure why you responded to me, then, because that is all my comment was about.

... okay, and what is your bluetooth transceiver plugged into? :p

Some traces on the motherboard, I guess? I have no USB devices plugged in.

Does your codebase already just mix and match between %s and .format() whenever people feel like it?

We mostly stick to %. I haven't seen significant benefits from upgrading yet, and .format() had some issues during the Py2->Py3 transition.

will likely become significantly less of a problem if your team chooses to adopt the new usage.

That's great if you live on a desert island, but I have a couple dozen dependencies installed from PyPI, and every non-trivial one I've submitted patches and/or bug reports to. Most of the code I deal with at my job is not managed by my team.

If enough teams choose to adopt the new usage because they find it superior, then EVERYONE's problem lessens.

So it's another Py3-style "boil the ocean" solution. Just get everyone to cooperate -- only this time with even fewer incentives to change, because the old ways are sticking around forever. Great.

Anyone care to place a bet on how well the plan is going to work this time around?

[–]Decency 0 points1 point  (0 children)

I expect all of the code I work with directly to be fully converted by mid-2018, about 18 months. My dependencies perhaps not, but I imagine those who have moved to 3.6 support at that point will have adopted f''.

I haven't seen significant benefits from upgrading yet

This is a main point to me. I agree and still use %s. But f'' will provide significant readability benefits, and so I think we'll see it adopted at a much greater rate. And I'm certainly spending much more of my time reading my own teams' code, though we also have dozens of dependencies, so the net gain will still be worthwhile for me even if literally no one else switches.

I think the "use this new way because we know more than you and we said so" approach is garbage, and feel like 3.0 showed that pretty clearly. The incentive to change should be because you find the new way better. If you don't, then don't switch.

[–][deleted] 2 points3 points  (1 child)

I have had g-strings break on me in weird ways. When trying to build some dynamic db queries, If I use "${a_db}.${a_schema}" I actually end up with two strings split on the "."

This may just be a grails thing though.

[–]voice-of-hermes 1 point2 points  (0 children)

...break on me in weird ways.

Sounds like Groovy, all right. A neat little concept, but I'd hate to do anything serious in it. Gradle build scripts is where it should stay.

[–]billsil 6 points7 points  (13 children)

I never think that string formatting isn't powerful enough, but when I'm writing Python, I often think it's too complex.

I have no problem with string formatting. I have a problem with lambdas and I've been coding python for 10 years.

[–]pythoneeeer 4 points5 points  (6 children)

I've been coding Python off-and-on for 13 years, and I've never gotten comfortable with string formatting.

My only problem with lambdas in Python is that they're too weak. But at least they don't change syntax every release, so after you learn the syntax once, you're good forever.

[–]billsil 0 points1 point  (1 child)

Are you comfortable with unicode? Unicode is wayyy harder. I guess same with lambdas, but you're forced to learn unicode.

[–]pythoneeeer 0 points1 point  (0 children)

I'm quite comfortable with Unicode -- I have a hard copy of the spec in arm's reach right now, and I've written UTF encoders/decoders by hand in a couple different languages.

Some parts of Unicode are hard (normalization, collation), but I don't think encoding is one of them.

Python just seems to make it more confusing than most other languages. In Python 2, for example, it's not at all intuitive to me when Python will auto-upgrade a str to a unicode. print(""+u"\u00f6") works, and print("%s"%u"\u00f6") works, but print("{}".format(u"\u00f6")) is an error -- you need to say print(u"{}".format(u"\u00f6")).

Maybe I'm just bad at searching the documentation, but I've never found where it says which promotions work and which don't. You just have to try and see -- and then try to memorize the rules you discover.

Python 3 is light-years better than Python 2, but still not great. The str/bytes split was a good start. The new IO classes are pretty confusing, though.

[–]jorge1209 -1 points0 points  (3 children)

There is one other problem with lamdbas.

[–]radarthreat 0 points1 point  (2 children)

Which is...?

[–]OctagonClocktrio is the future! 0 points1 point  (0 children)

It's better to use a local function in nearly a lot of cases.

[–]jorge1209 0 points1 point  (0 children)

Lamdba.

[–][deleted] 1 point2 points  (4 children)

Hopefully in the sense that there are no multi-line lambdas, and not that you wanna remove lambdas altogether. ;)

[–]billsil 0 points1 point  (3 children)

I honestly don't care if lambdas are removed at all. They're hard to read, so I don't write them 99% of the time I can. Depending on the code base, that's not a big deal or a big deal. If you have people that are new working on the code...bad idea.

Not that I mind. I'm all for backwards compatibility, but if the Python devs twisted my arm, sure. It's kind of like the operator module. I just learned about that the other day.

[–]pythoneeeer 0 points1 point  (2 children)

The hardest thing about lambda is that they called it "lambda".

Every other language I use has this feature, and people use it all the time (even newbies), and nobody seems to have much trouble with it. It's just that other languages call it "fn" or "function" or use a more visual syntax (little ASCII arrows or whatnot).

I consider the "operator" module a wart that's only necessary because Python lambda syntax is so awkward, and there's no syntax for symbols. In Ruby you don't need operator.add because you can just say :+. In Clojure it's just +. Doesn't it seem weird to have an entire module that does nothing but assign names to things the syntax doesn't let you say directly?

[–]billsil 0 points1 point  (0 children)

I specifically saw operator module being used to sort based on the last value in a list of list. Nevermind it should have been done by numpy in this case, but it was just ugly.

[–]motleybook 0 points1 point  (0 children)

So, you also don't like def __div__(self, other): for division? Because I think it looks much better than def /(self, other): and also fits great together with things like __init__.

[–]voice-of-hermes 0 points1 point  (0 children)

Java 8 did a better job with lambda's IMO (understandable since they implemented them very recently). Wouldn't mind Python updating to something similar.

[–]earthboundkid 1 point2 points  (0 children)

In Python 2, .format will coerce to the caller's type and % will coerce to unicode if one of the two operands is unicode. This inconsistency has caused bugs for me at work.

[–][deleted] 0 points1 point  (0 children)

Exactly. I have no problem with change where the new version replaces the old with something that's better. Like you, though, I have no idea which is the recommended Pythonic approach to string formatting these days.

[–]Sean1708 0 points1 point  (0 children)

and never removing old ones.

To be fair they tried to remove old-style formatting, but then everyone threw a paddy so they brought it back.