9 ms·
This is valid Python syntax
- dec0dedab0de 3y agoOh man, I seriously just wretched at the idea of putting an import in a lambda. That was sick, and awesome.
- Waterluvian 3y agoPython has so much potential for doing the most horrible things. I love it. I love seeing what people come up with.
- gonzo41 3y agoUntil you find them in production. Then you git blame and go take a long look in the mirror thinking about what you've done. In the early aughts the terseness of python seemed great. Now as I age I find i have less time for it.
- Waterluvian 3y agoI feel the exact same way. Python gave me so much flexibility and escape hatches when I did dumb design things as a novice. Now as a veteran I just want a boring, strong static language with clear boundaries and behavioural expectations
- sureglymop 3y agoI just always found it nice as a prototyping language. If you have an idea of how you want to do something or solve some problem, create a working prototype in python. Then create a more polished version in another language.
- IshKebab 3y agoI don't think terseness is the issue. It's the fact that it lets you do anything, even if it's a terrible idea. `__subclasses__` is my favourite example of that.
- ptx 3y agoStrange article. I thought it was going to show that these features interact in some unexpected way that causes weird bugs, but it all seems to work as expected. So the conclusion, I guess, is that code doesn't make a whole lot of sense when it uses every available feature for no particular reason and foreign languages for variables names?
- BiteCode_dev 3y agoAuthor here. There is no conclusion to the article, the weird syntax is just a mean to an end: - This makes people curious, so they read. - This gives an opportunity to show a lot of cool features of the language in a short format. As the post mentions, Python's learning curve is smooth but long, so it's nice to be able to introduce some tricks to coders that would otherwise not be exposed to them. - This demonstrates those things indeed, as you said, work together. Even if Python is a language on which a lot of things have been bolted on after the fact, it's still decently integrated. - This allow me to insert a warning about not abusing them because even if Python is a readable language, you can always go too far. It's good maintain this message in the community. - It's just fun, really. It's an article I would enjoy reading.
- agumonkey 3y agoas a semi plt nerd, i like to push grammars a bit deeper than average application, thanks and often indentation helps keeping track of structures
- babel_ 3y agoMust admit I wasn't quite sure what the point of the first one was. 0xBEAF is literally just 48815, as is 0x_B_E_A_F, as the numeric literals now ignore interleaved "_", and you just format it as a float after-the-fact as normal in an f-string. So why? There's nothing particularly special in the use of syntax here, so hardly a curious or weird artifact, let alone abuse. > * has a completely different meaning I'd personally rephrase this, as the idea around splatting is that it symmetrically can "gather" and "spread" multiple values, albeit with the syntactic limtations of starred expressions (likewise with *). You do seem to confuse syntax for semantics: [][:] = [...] is a semantic trick to avoid binding a sequence, if you did a = [][:] = [1,2] you'd find a == [1,2] because it just passes along the reference to the right hand side, so without another layer it's just skipping binding it, and that's the major trick with the second one, being that it "discards" the first value when destructuring this way (so long as that first value is a sequence). This can be done "better" with _, *_ destructuring, which probably weirds more people out to discover that shadowing in a statement is legal (and the unused/shadowed variable is "better" as it works even when the first value isn't a sequence, making [][:] more fragile). People with knowledge of other languages might get confused by _ being a variable, but that's a known stumbling block. Naturally, everyone has their own journey through discovering Python (or whatever language) and may or may not touch upon many parts of it syntactically and semantically. However, as far as "weird" syntax that has valid semantics goes, I'd say this is far from esoteric or unusual (where's the walrus?), and some parts are just the natural expression (such as with the splatting). Really, the only code smell I find here is the inlining of everything and redundancy (i.e. splatting a generator into a list)!
- svilen_dobrev 3y agomost of these are loooong time there. list[:] replacement is since ever. (used for reversing a list, when there wasnt .reverse method) But my beloved one, is the sequence of these changes, in time: ver.134: x = ('a', 2) some change in requirements made 2nd value unneeded, so it became: v167: x = ('a',) which is one day, "optimized" to: v193: x = ('a') Funny thing is, and x[0] will still deliver 'a' as it was before. Now if that 'a' is not constant but some parameter, and it's not 1 character long string..
- roelschroeven 3y agoThe third one is fundamentally different though. You probably know all this, but I thought it'd be a good idea to mention to prevent any confusion for other readers. Your second example: >>> x = ('a',) >>> print(type(x), len(x)) <class 'tuple'> 1 >>> print(type(x[0]), x[0]) <class 'str'> a x is now a tuple with 1 element, which is the string 'a'. (You don't need the parentheses, by the way: 'x = a,' works just as well. The comma is necessary though! Some people think tuples are created with parentheses, but parentheses are actually not required, except in the special case of the empty tuple.) Third example: >>> x = ('a') >>> print(type(x), len(x)) <class 'str'> 1 x itself is now the string 'a'. The parentheses didn't change anything; you can further 'optimize' to x = a without any change in meaning. x[0] still works because, as you mention, by accident x is a string with one character. It doesn't work in other cases: >>> x = ('gotcha') >>> x[0] 'g' >>> x = (3) >>> x[0] Traceback (most recent call last): File "<pyshell#59>", line 1, in <module> x[0] TypeError: 'int' object is not subscriptable
- 2h 3y ago> a, b = b, a > (don't know why this is so famous, I never, ever used that in prod) This allows you to sort an array in place. It's heavily used in Go: https://godocs.io/sort#example-package https://godocs.io/sort#example-package
- BiteCode_dev 3y agoYeah but in Python list.sort() takes care of that.
- falcor84 3y agoTrue, but every once in a while we want to sort something that isn't a list and/or with a custom sorting algorithm.
- babel_ 3y agoSwapping variables around is also really handy for patterns like "alter list a until stable", especially with the walrus := operator allowing you to avoid declaring b outside of the while loop, keeping the intent clearer.
- roelschroeven 3y agoA variant is useful to update two (or more) dependent variables in one go without having to introduce temporary variables. For example to calculate the greatest common divisor using the Euclidean algorithm: def gcd(a, b): while b > 0: a, b = b, a % b return a
- tgv 3y agoYou can always write something like {var tmp=a;a=b;b=tmp;} in any language if you want to sort in place.
- 2h 3y ago> in place that doesn't mean what you think it means.
- kkirsche 3y agoI learned about Python dev mode in this, thanks! For anyone else: https://docs.python.org/3/library/devmode.html https://docs.python.org/3/library/devmode.html
- BoppreH 3y agoI don't find that so bad. They look complicated but behave as expected, and would only be a problem if you inherit a code base from a lone wolf without common sense. Python has plenty of warts[1], but I don't think the syntax is one of them (yet). Some of the more concerning warts: # Mutable defaults in function parameters. def fn(arg=[]): arg.append(1) print(arg) fn() # [1] fn() # [1, 1] fn() # [1, 1, 1] # Loop variables are shared across executions. lambda_list = [lambda: print(i) for i in range(5)] for fn in lambda_list: fn() # 4 # 4 # 4 # 4 # 4 # Iterating over an exhausted generator yields an empty list. numbers = (i for i in range(5)) assert 1 in numbers print(list(numbers)) # [] # I hate this one because it could have easily been an error, it's # hard to track down, and lots of stdlib functions return generators. # Chained operators can sometimes be very unintuitive. should_be_ascending = True assert 1 < 2 == should_be_ascending # AssertionError because it was interpreted as # `(1 < 2) and (2 == should_be_ascending)` [1]: https://github.com/satwikkansal/wtfpython https://github.com/satwikkansal/wtfpython
- travisjungroth 3y agoOn the generator example, it doesn’t yield an empty list. It continues to return an empty iterator. This couldn’t have easily been an error because one aspect of generators is they can be dynamic. There could be an API call in there, where it polls for new results. Raising a “NoMoreForever” error or something would break the interface.
- BoppreH 3y agoYou're right, "empty list" is an imprecise description. But couldn't they have made it behave like Go channels, for example? Read it until exhaustion, then close it, so that subsequent reads (or calls to 'next()') fail with something other than StopIterator. I know it's not backwards compatible and hence a no-go now, but the original decision puzzles me.
- formerly_proven 3y ago1 and 2 are the scoping rules (since Python has no variable declarations [1], making all blocks scopes would be pretty awkward and require tons of "xy=None # reassigned later" and nonlocals), 3 should result in [2, 3, 4], not []. [1] It actually does nowadays, ever since non-assigning annotations came around.
- bandrami 3y agoAs a Perl guy none of that is even slightly distressing to me
- macintux 3y agoUnpacking used to be more powerful. I was mostly oblivious to the Python community when I started working on a project and discovered that you could unpack tuples in function heads, like Erlang, and started using that frequently. Then I discovered I was using Python 2 and it was EOL, and that Python 3 had dropped support for that “because no one uses it.” Maddening.
- randallsquared 3y ago> if you use "_" in hex, and interpolate it with a 2 decimal precision ...or if you don't. The '_' doesn't change anything or add anything, here.
- pedrovhb 3y ago> While not as powerful as destructuring in other languages (I still wish we could unpack dicts as JS does with objects) Like this? In [1]: a = {1: 10, 2: 20} In [2]: b = {3: 30, **a} In [4]: b Out[4]: {3: 30, 1: 10, 2: 20} Or pattern matching? In [6]: match b: ...: case {3: somethingelse, 1: foo}: ...: print(somethingelse, foo) 30 10
- babel_ 3y agoI think they mean {a: x, b: y, *c} = d, which is a nice idea now we have a stable order to dicts, but I've rarely come across times when I needed this that I can't simply use .items() and such for, or care about more general pattern matching like values for specific keys, which you can just do a, b = d["a"], d["b"] with little loss of clarity, sure {"a": a, "b": b} = d is cute and all, and maybe we'll oneday see it now we have match as syntactic precedent, but I don't really feel concerned by its absence.
- tuukkah 3y agoThey mean you can unpack lists: a, b, *rest = args So why not dicts: "a": a, "b": b, **rest = kw_args Also, in JS the destructuring does not fail because of extra elements even if you don't capture them. Further, in JS you don't need the quotes, and a: a can be shortened as simply a. You'll need the dict curly braces though. Thus the same in JS, which you see a lot e.g. for properties of a component in React code: { a, b } = props; It's quite nice with no excessive "line noise" and even makes up for JS functions lacking real keyword arguments.
- BiteCode_dev 3y agoAlso JS allows you to define default values if the key is missing, and Python's pattern matching doesn't.
- lysecret 3y agoBiggest footgun in python IMO are mutable default parameters. It amazes me that someone thought this was a good idea.
- lysecret 3y agoAlso a most random note here: mutable /ˈmjuːtəbl/ adjective liable to change. I like the emphasis that it’s liability haha
- Spivak 3y agoThat's not really what's going on. Python has default values where, say Ruby, has default expressions. def myfunc(x=[]) puts x end In Ruby is the same as def myfunc(x=lambda:[]) print(x()) in Python when the default is needed. The benefit is performance, you only have to evaluate the default once at function definition time. I won't say it's not a footgun but it's also I think a sane choice when in Python it's common to have functions with many default parameters.
- o1y32 3y ago"the benefit is its performance" Sure, but when you design a language you are striking a balance between many things (which is why some features could take years to finalize and implement). In this case, the gain in performance is likely minimal, but the confusion and surprise it causes does a lot of damage. (I recently read through the entire series of conversation with Anders Hejlsberg on the design of C# which is very insightful https://www.artima.com/intv/anders.html https://www.artima.com/intv/anders.html)
- crabbone 3y agoYou are too naive when you think that someone thought about it. Many things in Python happened because that's how they were implemented initially without much thinking or at all. There was no plan, and, in the initial stages, I'd imagine that the author(s) were surprised that they'd even gotten this far. Famously, the whole object system in Python was implemented in like a few days. That included the design and testing too... It did see some changes over time, but there's a lot of petrified turd there that's increasingly difficult to get rid of.
- jonnycomputer 3y agoGod, is this going to be what I see in my next python coding interview?
- 1024core 3y agoPython is slowly morphing into Perl... :-D
- actionfromafar 3y agoSomeone™ should make a Python-to-Python compiler which converted all normal constructs into weirdness like this.
- uncharted9 3y agoFor me, one of chatGPT's most frequent uses is to convert all of these one-liner statements into logical multi-line code that my feeble brain can process.
- guenthert 3y ago> Inverting variables: Non-native speaker here and even after 20+years every once in a while it bites, but I know that as 'swapping variables'. Where does the inverting come from? Related to the 'inverting a tree'?
- coke12 3y agoIt's possible they are trying to avoid making people think of memory swapping which is existing jargon and usually means something more complex.
- janderland 3y agoI also know it as “swapping” variables and I’m a native speaker.
- EddieJLSH 3y agoFun syntax gotcha that I saw recently was a YAML MAC address that were all numbers like 11:22:33:11 which Python was parsing as a sexagesimal number so some libraries were throwing type errors
- dinom 3y agoLol, and they say Perl is bad. https://www.perl.com/pub/2000/01/10PerlMyths.html/#Perl_looks_like_line_noise https://www.perl.com/pub/2000/01/10PerlMyths.html/#Perl_look... This isn't meant as flamebait, I'm just pointing out you can write obfuscated code in any language.
- charcircuit 3y ago数字 is numeral. It doesn't make sense as a translation for numbers in that function.
- bionhoward 3y agoLol, your definition of “valid syntax” is wayyy different from mine. I wouldn’t ever use crap like that, it’s too unreadable to be “valid syntax” even if the interpreter can handle it. Can we all collectively agree to include our own brains in the definition of “valid code?”
- eCa 3y agoValid code != best (or even good) practices.
- bettercallsalad 3y agoWe had a dev in our team who would love to use these one liner cryptic expressions and challenge the minds of code reviewers. It was all fun until he left and others had to refactor bunch of code. One liners like that often have very little benefits IMHO.
- userbinator 3y agoThat example with the Chinese characters brings up a point that seems to have gotten lost or actively suppressed, likely by those with other motives: what may look "unreadable" to you may in fact be something that is perfectly normal to billions of others. Yet why are people constantly told to emphasise "readability" in the code they write, which really refers to a lowest-common-denominator dumbing down approach? Shouldn't the onus be on the reader to educate oneself sufficiently in the language to understand code that one thinks is "unreadable"? Moreover, unlike natural languages, programming languages are defined exactly and systematically, so there is no ambiguity whether a piece of code is valid or what it does. What is "unreadable" to some could be perfectly readable to others; and what is readable to some could still be readable but unnecessarily verbose and tedious to read to others. We shouldn't let how we write code be dictated by those who barely know the language and want to drag us down to their level.
- lamontcg 3y agoWhich is why I think languages need UTF-8 identifiers and arbitrary UTF-8 operators. Most people working on webdev in the USA should never touch them and should feel free to ban them in their linters. But people who speak and write foreign languages with other alphabets and domain experts and people who just want to use π or µ or ∆v or whatever because they feel it makes their code clearer should be able to. Mathematicians who have PhDs should be able to write arbitrarily complicated heiroglyphical operators that I probably won't choose to understand in this lifetime. And that's okay, I don't need a language that attempts to guarantee that I can always read it, that is probably literally impossible and just constrains it unnecessarily.
- schoen 3y agoCoq has an interesting compromise, where the actual code passed to the compiler is in ASCII, but one can define sequences which have alternative renderings (usually to attempt to visually match existing math notation). For example, one could write Theorem demo: forall (x: nat), (exists y, x = S y) -> x <> 0. In many editors, this will get rendered as Theorem demo: ∀ (x: ℕ), (∃ y, x = S y) → x ≠ 0. This looks a lot nicer for mathematicians. (S here is Peano successor.) One minor nuisance is that, without an additional plugin, you can't actually write source code literally using the Unicode symbols (they're normally display-only, not part of a source file). The plugin to allow one to type them explicitly (with a Unicode-aware input method) does exist, but would make one's source code not portable for other users. Also, this is really specifically for math, not internationalization, and doesn't provide a helpful model for people wanting to type identifiers in non-ASCII-supported scripts.
- da39a3ee 3y agoThis is neat! img = ["r", "g", "b"] * 10_000_000 iterator= iter(img) groups= zip(iterator, iterator, iterator) I've been writing Python for 15 years and it had never occurred to me that I could do it like that. Honestly I think I'd have reached for more_itertools.chunked. (FWIW In R I think I used to set an array dimension on the vector and process rows.)