43 ms·
Coming from Perl, I used to want this badly, but then I thought that there's absolutely nothing wrong with m = re.match(...) if m is not None:
by drdaeman 7y ago
Coming from Perl, I used to want this badly, but then I thought that there's absolutely nothing wrong with
m = re.match(...)
if m is not None:
pass
Now, I wonder what you meant saying that single-line version is less error-prone, because I don't think so. I believe they're exactly the same in this regard, except for a bizarre case when someone would bastardize the code by putting some irrelevant lines between the assignment and the comparison, obfuscating the logic.
Also, as I tend to use `if m is not None:` rather than just `if m:` (because I typically don't want to run all that subtle `__nonzero__` magic), it would - subjectively - look less pretty in the one-line version: `if (m := re.match(...)) is not None:`.
Use in the generator expressions to create aliases/shortcuts and avoid repetition is great, though. I love this use case.
- 3PS 7y agoThe issue, in my opinion, is when you want something like this: while m := f(many, args): # do stuff with m Now, if you're writing this in Python 3.7, you often end up with some code duplication: m = f(many, args) while m: # do stuff with m m = f(many, args) # duplicate Or something like this: while True: m = f(many, args) if not m: break # do stuff with m Personally, I consider the last version to be the most elegant, but still something of an anti-pattern. So I welcome this new operator.
- hermitdev 7y agoI literally wrote your last example twice last week to hash files in 4k chunks. Those 4 lines would be reduced to 1 with the walrus operator. I welcome it, as well. Edit: 1 line, not 2 (excluding the "do stuff")
- cellularmitosis 7y agoThis comes up in recursive descent implementations as well https://gist.github.com/cellularmitosis/53913ad229bad5d0a3bb92f196fdda7b https://gist.github.com/cellularmitosis/53913ad229bad5d0a3bb...
- ygrechuk 7y agoWhy not something like this: def f_iter(many, args): while True: m = f(many, args) if m: yield m else: raise StopIteration ... for m in f_iter(many, args): # do stuff with m This way you’re isolating all the initialization logic, error handling, etc. And you can focus on your domain logic in your client code.
- repsilat 7y agoAnd less reasonably, something similar for regular expressions: for m in filter([re.match(...)], bool): ... ;-)
- mlevental 7y agoso i should write a whole other function that iterates over a list instead of being happy that := exists?
- throwaway744678 7y agoYes! No pain, no gain!
- ciupicri 7y agoInstead of raising StopIteration you could also just return from the function. You could also make it a bit shorter if you swap the if branches. def f_iter(many, args): while True: m = f(many, args) if not m: return yield m
- zb 7y agoInstead of raising StopIteration you have to return from the function, otherwise you'll just get a RuntimeError starting with Python 3.7.
- ciupicri 7y agoYou're right. The [changelog] for Python 3.7 states: > [bpo-32670]: Enforce [PEP 479] for all code. This means that manually raising a StopIteration exception from a generator is prohibited for all code, regardless of whether ‘from __future__ import generator_stop’ was used or not. [changelog]: https://docs.python.org/3.7/whatsnew/changelog.html#id115 https://docs.python.org/3.7/whatsnew/changelog.html#id115 [bpo-32670]: https://bugs.python.org/issue32670 https://bugs.python.org/issue32670 [PEP 479]: https://www.python.org/dev/peps/pep-0479/ https://www.python.org/dev/peps/pep-0479/
- xapata 7y agoThat last one isn't an anti-pattern. That's the pattern concluded best for do-while in the discussion of PEP-315. https://www.python.org/dev/peps/pep-0315/#notice https://www.python.org/dev/peps/pep-0315/#notice
- pyrale 7y agoThe last version clearly exposes that you have an infinite loop. This is something hidden by the `while(evaluate)` expression. Also if it comes to pure form I would have preferred `while (evaluate) as x:` out of establishing a parallel with existing syntax, but that's not very important.
- pdpi 7y agoOn the contrary: the loop isn’t fundamentally infinite, it’s just an artefact of the language that you have to write it that way. The walrus lets you put the termination where it belongs.
- pyrale 7y agoI don't understand that the walrus has any significance with regards to the `while` loop termination. it's the nature of the evaluated expression that makes a difference, more precisely, whether it will return something that is cast to False. In that sense, while loops over expressions always require knowledge of the expression to understand the semantic. On the other hand, something like: while True: with x as y: print(y) would not require understanding of x to understand the intent. Therefore it is not semantically equivalent to: while x as y: print(y) Note that in both cases, `:=` is strictly equivalent to `as` as it is defined for `with`. That is a matter of personal preference.
- Alex3917 7y agoIf you only have one or two regex like that then there is no difference in readability. If you have ten in a row though then suddenly your function no longer fits on the screen and it’s more difficult to understand what’s going on.
- bandrami 7y agoI mean, what you really want in this case is Perl's =~ operator, as well as the implicit $_ variable.
- eesmith 7y agoThink about if you match multiple patterns: for line in f: if (m:= pat1.search(line)) is not None: ... do stuff .. elif (m:= pat2.search(line)) is not None: ... do other stuff .. elif (m:= pat3.search(line)) is not None: ... do something else .. In older Python that's: for line in f: m = pat1.search(line) if m is not None: ... do stuff .. else: m = pat2.search(line) if m is not None: ... do other stuff .. else: m = pat3.search(line) if m is not None: ... do something else .. I think the newer makes it clear that it's supposed to be a simple elif chain, where all branches following the same structure. There are other ways to structure it, but the alternatives I can think of also have their own cumbersome complexities.
- kstrauser 7y agoThat's... lovely. I propose making this the standard example of why you might want to use the walrus operator.
- drdaeman 7y agoIf actions are different, I tend to put them in functions (independent testable pieces, yay!) and iterate over a mapping: LINE_ACTIONS = ( (re.compile("pattern 1"), do_stuff), (re.compile("pattern 2"), do_other_stuff), (re.compile("pattern 3"), do_something_else), ) ... for pattern, action in LINE_ACTIONS: m = pattern.search(line) if m is not None: action(m) break Or even use a method registry pattern that would auto-populate LINE_ACTIONS by just declaring the fuctions: @action("pattern 1") def do_stuff(m): ... Alternatively, I might just break the processing into a function: def _process(line): m = pat1.search(line) if m is not None: ... do stuff ... return m = pat2.search(line) if m is not None: ... do stuff ... return m = pat3.search(line) if m is not None: ... do stuff ... return _process(line) Of course, this depends on the purpose. Could be completely inadequate in some situations.
- eesmith 7y ago