I spent the last few evenings, starting Tuesday, rewriting the front-end of this site in Python (using Flask). We all know that Python is a big deal lately, and has been for quite a long time. My exposure to it has been minimal; I've ported some scripts at work from Python to PHP (don't hate me, please, it wasn't my decision), maintained a few others, and written some non-work scripts for general computer science concept learning. Aside from that, I've mostly ignored its existence.
There are lots of languages to choose from to tackle web programming tasks. Even after this exercise, I don't know that I'll readily jump to Python for all of my needs. From a business perspective, most of my work utilizes PHP (sometimes Java or even C# if I'm feeling especially rogue), and I have the most familiarity in that realm. I'm trying not to let familiarity cloud my judgement, though, because some frustrations are not due to language limitations but to non-standard approaches or simple ignorance of a language's nuances. I have around seven years of Coldfusion experience, but you won't hear me extolling its virtues anytime soon, so I have to think that I can partially leave my familiarity bias at the door to assess a technology.
I'll spare us all another set of good/bad/neutral bulleted lists since the internet has an overabundance of those. Here are some general thoughts from my experience in setting up my environment from the ground up, developing in Python, and pushing the whole thing to the server.
Setup and Tooling
I won't win any awards for having used Windows throughout this process. Python seems more naturally suited to use on Unix/Linux environments. I, however, could not be bothered to remove the laptop that was already on my lap and shamble five feet to the iMac on my desk. It was easy enough to make sure that my environment had Python 3 and all its necessities installed. I suppose this is where someone would talk about virtualenv and pip and all that. Virtualenv seems like a nice concept, but this is my one Python project right now and it's using the latest Python version which is the system version, so it doesn't solve any problems for me right now. And Pip just works, so what else needs to be said?
One thing that I need to address is a painful problem that I encountered setting FLASK_ENV=development. It broke the built-in server entirely, and I couldn't figure out a solution to the problem. That meant I was stuck with production environment settings, which entailed a lot server kicking with Ctrl + C to kill the process followed by flask run to restart it. I'm sure that there are tangible ways to deal with this or to resolve my error, but the half hour of digging didn't unearth anything useful and I was in no hurry to continue the misery of debugging a problem that shouldn't have been happening straight out of the box.
Aside from that I was able to figure out most of my hangups in short order with some google searches. The development process was generally the same experience as with any other language. I do find the Python documentation to be mildly underwhelming at times, but that's certainly not a critique reserved only for this language. PHP gets that levied at it as well. The Flask documentation got me where I needed to go, so no complaints there, and when I needed to change the static folder it was painless enough to find what I needed in the Jinja2 docs, bolstered by some StackOverflow commentary as well.
Language Quirks (Features?)
Probably the most distinctive difference between Python and other languages that I've used was certain state persistence between requests. Notably, I was setting a dictionary that acts as a definition for the page - it holds all the junk that the page needs to render properly. It contains a list of CSS files which the Jinja template should simply loop over, generating a <link> for each list element. Various pages on the site have custom CSS files, so in the route function for those pages I figured I could just add that page's CSS file to the list. My mistake was assuming that the dictionary would get reset on each page load. Ultimately I ended up with special-case CSS being included on every page subsequent to landing on the page with that custom CSS include. Instead of trying to remove the list item or otherwise keep track of them with additional logic, I just whipped together a function that returns the default dictionary and each route function calls it to get the page definition which can be added to as necessary.
Another feature (?) I ran into happened when loading content from files. Apparently file loading will default to unicode encoding which you then need to probably convert to utf-8 or otherwise. That's by no means a problem, I just found it to be a curiosity, and it was definitely a nuance that I needed to take some time to figure out how to get around.
I generally dislike the fact that you need to specify self as the first argument in class functions. I'm not sure why this can't be implicit; you don't have to actually pass it in when you call those functions, so why does it need to be explicitly defined that way?
What's with the lack of constants? What's with the lack of actual data hiding capability? I'm probably one of the first people to say "Just don't be stupid when you code," but realistically I and everyone else codes stupidly from time to time. Just because I have an implicit understanding of my own personal programming practices and the code that I wrote myself doesn't mean that the next person who needs to maintain it will. So while I think that data hiding and constants could largely be superfluous, and I don't heavily rely on private/protected members unless I'm building a publicly-consumable API where I actually want to not worry about users munging my variables, being able to explicitly and statically define something as const or private would be nice. I understand that Python has underscore notation for pseudo data hiding, but first of all I think that the convention is gross and only exists because your IDE isn't going to tell you the protection level because it's inept so the solution is really a hack, and secondly it doesn't really hide anything if someone comes along and wants to get at that variable or function.
"PHP sucks, it only has one container type." Yes, I completely understand. But it's actually a robust container that has a ton of functions surrounding it, and since you can use string indices, it makes it a dictionary just as much as an array. How many containers do you actually need? Be honest. That said, I like that Python has some more container types. I like that you can return a tuple. It's all handy, and it's pretty cool. I guess this is one area where I recognize that my familiarity bias is present; I've gotten along with PHP's ArrayDictionaryThing for so long that having a huge container selection doesn't matter to me. C++ has a massive collection of container options and most of the time I just can't be bothered to care. But +1 to Python for having a few realistic and useful options in that regard.
My production environment is Ubuntu running Apache, so I easily installed mod_wsgi, reconfigured the VirtualHost, and was immediately successful! Not. While the Apache configuration truly was simple, I ran headlong into what I didn't immediately recognize as a pathing issue. I'm not certain if it was a variance was Windows vs Linux or the built-in server in Flask vs Apache (the latter probably being the most likely to my mind), but it was a painful experience debugging on the production server. I know - the right way would have been to set up everything in a VM (or on an unused domain), but I couldn't be bothered to go through the process, and nobody reads my site anyway, much less in the middle of the night when I was working on the migration. Suffice to say, starting from an absolute path when loading files solved my problem:
Setting up the server and pushing changes was otherwise uneventful, thank goodness.
I may continue this quest so I can have a more rounded understanding of web programming with Python. How much can you really learn from ~150 lines of code ported over from another language? It wasn't a direct port since the predecessor was homegrown PHP, but it follows generally the same concept in Flask with Python conventions. There is an administrative portion of the site to write and edit posts that I could port over so I glean some experience with form handling and all the glory that comes from a less static app experience, and I'll probably take advantage of that.
Would I necessarily pick Python for my own uses? I'm not sure. I just don't have the full breadth of experience yet to make that determination. I don't think I'll be using it for gamedev anytime soon, if ever, unless I go full-on roguelike nerd. I still have a lot of interest in .NET Core, and I do like statically typed, compiled languages. I like C-style syntax; anything that looks like glorified Basic kind of makes me cringe. But to each their own. I'd also like to get some hands-on time with Go, even if it's not any kind of industry standard. To be honest, I don't care about industry standards on my own time, I care about what's enjoyable. J2EE Spring applications aren't that.