4 ms·
Python as a second language empathy (2018)
- zahlman 2y ago> Who here has another language that they knew pretty well before learning Python? (most hands go up) Great! Terrific! That’s a superpower you have that I can never have. I can never unlearn Python, become fluent in another language, and then learn Python again. You have this perspective that I can’t have. It felt a little strange to read this. I knew many languages "pretty well" (or would have said so at the time) before Python, but it's been my primary programming language pretty much the entire time since I first picked it up 20 years ago. So I do have that perspective (I also spend a fair amount of time thinking about PL design in the abstract), but I don't feel "PSL" at all. > Does the value stored in `a` know the name of that variable? Okay, I'm stumped - in what other language would it "know" (I assume this means automatically recording the string "a" as part of the object's state in some way)? Or is this just referring to debugging information added in some environments? > Python = is like Java, it’s always a reference and never a copy (which it is by default in C++). Java has primitive types, for which it is a copy. But it's more accurate IMO to attribute this as a property of names, rather than a property of assignment. Python's names (and most Java values, and some C# values) have reference semantics. Having understood that properly, saying that Python (like Java) is "pass by assignment" then just follows naturally, and all the usual confusion around this point vanishes. Passing an argument to a function is simply assigning it to the corresponding parameter. (C++'s idea of "references" in the type system is far more complex IMO - e.g. https://langdev.stackexchange.com/q/3798/_/3807#3807 https://langdev.stackexchange.com/q/3798/_/3807#3807 .) > We have both reference counting and garbage collection in Python. This is strange phrasing, because reference counting is a garbage collection strategy. I assume that here "garbage collection" is meant to refer to mark-and-sweep etc. strategies that operate in a separate pass, timed independently of the explicit code. But also, the reference-counting system is a CPython implementation detail. (Although it does look like PyPy tries to emulate that, at least: https://doc.pypy.org/en/default/discussion/rawrefcount.html https://doc.pypy.org/en/default/discussion/rawrefcount.html ) > I’ll translate this one to the transcript later - for now you’ll have to watch it because the visual is important: explicit super. Raymond Hettinger's video is also a useful reference here: https://www.youtube.com/watch?v=EiOglTERPEo https://www.youtube.com/watch?v=EiOglTERPEo > It takes less characters in Python! This checks out, but it would be fairer to show what it looks like on the declaration side rather than just the usage side.
- ForTheKidz 2y ago> This is strange phrasing, because reference counting is a garbage collection strategy. Oof, don't open that can of worms. Let me summarize the result: 1) the distinction is arbitrary and 2) people are very passionate about their personal view being considered canonically correct. To the extent i've started referred to refer to both mark-and-sweep/boehm garbage collection and reference counting as "automatic memory management".
- KerrAvon 2y agoI think people get unhappy that their favorite language doesn’t have real GC and it becomes an extension of their language advocacy. Python didn’t have GC for a long time.
- ForTheKidz 2y agoLet's just agree to disagree. I don't see any value fighting over these words.
- makeitdouble 2y agoNot everyone wants GC either, so the mix of feelings must have been complex. I didn't much follow these kind of holy wars, but I've seen people leave Java partly to get away from GC, same as people in the Objective C community being pretty happy to get ARC instead of heavier GC systems.
- MrJohz 2y ago> Java has primitive types, for which it is a copy. But it's more accurate IMO to attribute this as a property of names, rather than a property of assignment. Python's names (and most Java values, and some C# values) have reference semantics. Having understood that properly, saying that Python (like Java) is "pass by assignment" then just follows naturally, and all the usual confusion around this point vanishes. Passing an argument to a function is simply assigning it to the corresponding parameter. (C++'s idea of "references" in the type system is far more complex IMO - e.g. https://langdev.stackexchange.com/q/3798/_/3807#3807 https://langdev.stackexchange.com/q/3798/_/3807#3807 .) I use pass-by-label for this term, after a visual demonstration of variables that showed them as labels that attach to different values/objects. But I like "pass-by-assignment" a lot — it gives the correct intuition that whatever is happening when I do `x = 5` is also happening when I do `def func(x): pass; func(5)`.
- daveguy 2y agoTitle needs (2018). Data on language usage growth goes to 2018.
- zmmmmm 2y agoI consider pretty much anything written pre-LLM about learning languages to be dated these days. LLMs don't necessarily help you learn but the perform a complete transformation on the challenge of doing it to an entirely different problem space.
- a_e_k 2y agoOddly, trying to occasionally place on the Advent of Code global leaderboard was the thing that got me most fluent in Python as its own language. Before that, I tended to write Python in a much more imperative style, since C++ is my main language; think for loops and appending to lists. Thanks to AoC, I'm now addicted to comprehensions for transforming and filtering, know the standard library cold, and write much more concise, Pythonic code.
- dbcurtis 2y agoHa ha, yeah, for sure. It is easy to tell the Python code coming from an experienced C programmer. The thing about Python is, you can just give C a semi-colon-ectomy and it pretty much works. But it isn't Python... it took me a while to get completely pickled in the Python way of doing things. Ramalho's book did more than anything to put my brain into Python mode.
- ddejohn 2y agoPython's standard library is phenomenal.
- sgarland 2y agoAnd vast. IME, people often underestimate what it can do. For example, if you need to store IP addresses in a DB, you can use the stdlib library ipaddress to validate them, then store them as an INT, casting it back to dotted-quad with Python if needed. Also, of course, Postgres has a native inet type, which performs validation and other operations, storing a v4 in 7 bytes – not quite as good as 4 bytes for an INT, but much better than the maximum 16 bytes to store a dotted-quad as a VARCHAR. But if you’ve got MySQL (or anything else without a native type), this is a solid way to save space – and more importantly, memory – that can add up over hundreds of millions of rows (session information for a large SaaS product, for example).
- planckscnst 2y agoI learned several languages before Python, but the one that made it the most difficult was Ruby. After having done Ruby for years and becoming really familiar with idioms and standard practices, coming to python feels like trying to cuddle with a porcupine. List (or set, or dict) comprehensions are really cool... one level deep. The moment you do 'in...in' my brain shuts off. It takes me something like 30 minutes to finally get it, but then ten seconds later, I've lost it again. And there are a bunch of other things that just feel really awkward and uncomfortable in Python
- whytevuhuni 2y agoThere's a pretty easy trick to nested comprehensions. Just write the loop as normal: for name, values in map_of_values.items(): for value in values: yield name, value Then concatenate everything on one line: [for name, values in map_of_values.items(): for value in values: yield name, value] Then move the yield to the very start, wrap it in parentheses (if necessary), and remove colons: [(name, value) for name, values in map_of_values.items() for value in values] And that's your comprehension. Maybe this makes more sense if you're reading it on a wide screen: [ for name, values in map_of_values.items(): for value in values: yield name, value] ^^^^^^^^^^^^^^^^^ /------------------------------------------------------------------------------/ vvvvvvvvvvvvv [(name, value) for name, values in map_of_values.items() for value in values ]
- JodieBenitez 2y agoI write Python every day and I would have just write the "normal" loop which is the only one that is really readable in your examples, at least to me.
- whytevuhuni 2y agoI wrote it on one line to make the example make sense, but most Python comprehensions are plenty readable if they're split into many lines: new_list = [ (name, value) for name, values in map_of_values.items() for value in values ] The normal loop needs either a yield (which means you have to put it inside a generator function, and then call and list() the generator), or you have to make a list and keep appending things into it, which looks worse.
- svilen_dobrev 2y agoi have taught quite a few people of various backgrounds and levels. And still provide a short crash-course, once in a while. IMO in this article the elephant is missing, esp. for C/C++/java people: there are no variables as storage-spaces in Python. There are names/labels in namespaces, attached/referencing some value (stored in VM), and that is taken/examined at runtime-usage of those labels/names. The values themselves are living on other side of the fence, inside the virtual machine. The most important piece of the language Python in docs is the execution model, and in it, the "naming and binding" part: https://docs.python.org/3.11/reference/executionmodel.html#naming-and-binding https://docs.python.org/3.11/reference/executionmodel.html#n... i.e. what constitutes a namespace, how are they stacked at declare-time or run-time (visibility/shadowing), lifetimes, scopes and all that. The most usual gotcha is that inside of class xyz: ... is only a temporary namespace - and becomes class' attributes when closed (well, subject to metaclass behaviour). i usually present the "storage" as "imagine 2 notebooks of ~transparent-sheets" : one is the names' visibility (so called "variables") - namespace-hierarchy, where each sheet is one namespace level, and you see only the inner-most page that has something in certain row - shadowing all those towards the root at module level; the other is the attribute-on-something visibility, where the instance (if any, of whatever type) is (only one and) always on top, and then inherited classes (not instances) underneath one under another. Also, all instance-or-static methods in Python are virtual, i.e. using the topmost available - while class-methods are not, they use the specified-or-below but not above. Frankly, took me ~10 years, occasionally rereading that chapter each time when hitting new category of errors, to grasp the whole of it. Each comma there matters. Take your time. Edit: this "names-only" thing is similar in javascript, and other interpreted languages. Python also exposes the whole VM-runtime - from programmer's side of the fence - i.e. locals() is the current namespace, inspect.stack() gives the current namespace hierarchy, etc. Self-reflection can be taken quite far
- jebarker 2y ago> The most important piece of the language Python in docs is... Asking as an intermediate python programmer (in my assessment!): what is the importance of knowing this? Is it essential to know to _use_ python correctly or to _understand_ the language implementation?
- xg15 2y ago> But annotations are much less powerful than Python decorators. Coming from Java and seeing what kind if stuff is practically done there with annotations (looking at you, Spring), Python decorators actually feel more constrained than annotations, in a fully beneficial sense. Yes, Python decorators run arbitrary code, but at least, there is a straight-forward relationship between the decorator and the location of that code. Decorators are just syntactic sugar for a function call that takes the decorated function as an argument - so you just have to look up the definition of the decorator to find out what it does. In contrast, a Java annotation does exactly nothing by itself - it's literally a piece of metadata stapled to a function or field or argument, etc. This metadata is accessible either at compile-time using compiler postprocessors, or at runtime using reflection. The code that accesses the annotation and implements its behavior can be essentially anywhere in your class path. In the worst case, you'll have to go through every single usage site of the annotation to find the place where its behavior is defined. (As a bonus, annotations can be annotated themselves, too - so you'll potentially have to repeat the process recursively)