Conditional with statements
I'm not sure if this has been asked / suggested before. I'm wondering if there is any interest in conditional or loop-based `with` statements. I think it could be done without a syntax change. ### Napkin proposal Context managers could define `__if__` or `__for__`, and if those dunder methods were defined, then the `with` statement would either behave like a conditional or a loop. If `__if__` was defined then ``` with Ctx(): print('hi') ``` would only print `hi` if `__if__` returned True. This doesn't require a syntax change. The `__for__` variant would likely need a minor syntax change. ``` with item in Ctx(): print(item) ``` The `__for__` method is a generator that generates arguments of a loop. The item will be printed as many times as there are items generated by `__for__`. ### Use Cases This would simplify usage of my Timerit and ubelt library. The timerit module defines ``timerit.Timerit``, which is an object that is iterable. It has an ``__iter__`` method that generates ``timerit.TimerTimer`` objects, which are context managers. >>> import math >>> from timerit import Timerit >>> for timer in Timerit(num=200, verbose=2): >>> with timer: >>> math.factorial(10000) The timer context manager measures how much time the body of it takes by "tic"-ing ``__enter__`` and "toc"-ing on ``__exit__``. The underlying object has access to the context manager, so it is able to read its measurement. These measurements are stored and then we compute some statistics on them. Notably the minimum, mean, and standard-deviation of grouped (batched) running times. Unfortunately the syntax is one line and one indent bulker than I would prefer. However, a more consice version of the synax is available. >>> import math >>> from timerit import Timerit >>> for _ in Timerit(num=200, verbose=2): >>> math.factorial(10000) In this case the measurement is made in the `__iter__` method ``Timerit`` object itself, which I believe contains slightly more overhead than the with-statement version. (I should test to determine if this is the case). In the case where it does make a difference, a cool syntax might look like: >>> import math >>> from timerit import Timerit >>> with timer in Timerit(num=200, verbose=2): >>> math.factorial(10000) The other case is that my ``ubelt.Cacher`` library. Currently it requires 4 lines of boilerplate syntax. >>> import ubelt as ub >>> # Defines a cache name and dependencies, note the use of `ub.hash_data`. >>> cacher = ub.Cacher('name', cfgstr=ub.hash_data('dependencies')) # boilerplate:1 >>> # Calling tryload will return your data on a hit and None on a miss >>> data = cacher.tryload() # boilerplate:2 >>> # Check if you need to recompute your data >>> if data is None: # boilerplate:3 >>> # Your code to recompute data goes here (this is not boilerplate). >>> data = 'mydata' >>> # Cache the computation result (pickle is used by default) >>> cacher.save(data) # boilerplate:4 But a conditional ``with`` syntax would reduce boilerplate to 3 lines. >>> import ubelt as ub >>> with ub.Cacher('name', cfgstr=ub.hash_data('dependencies')) as cacher: >>> data = 'mydata' >>> cacher.save(data) >>> data = cacher.data I'm sure there are a lot of viable syntax variations, but does the idea of a conditional or loop aware "with" statement seem like a reasonable language proposal? -- -Dr. Jon Crall (him)
On Sat, Dec 12, 2020 at 11:42 AM Jonathan Crall <erotemic@gmail.com> wrote:
I'm not sure if this has been asked / suggested before.
I'm wondering if there is any interest in conditional or loop-based `with` statements. I think it could be done without a syntax change.
### Napkin proposal
Context managers could define `__if__` or `__for__`, and if those dunder methods were defined, then the `with` statement would either behave like a conditional or a loop.
If `__if__` was defined then
``` with Ctx(): print('hi') ```
would only print `hi` if `__if__` returned True. This doesn't require a syntax change.
This part has been proposed before: https://www.python.org/dev/peps/pep-0377/
The `__for__` variant would likely need a minor syntax change.
``` with item in Ctx(): print(item) ```
The `__for__` method is a generator that generates arguments of a loop. The item will be printed as many times as there are items generated by `__for__`.
Not sure that this one has, but it's basically just a context manager and a for loop, so I'm not really sure how much you'd gain over just using the two constructs independently, given that there'd then be massive confusion over "when should I use 'with item in thing' and when should I use 'for item in thing'?". For many use cases, it may be best to write the body as a function, which can then be called more than once. You can decorate a function in order to do whatever you like, and the return value from the decorator could be whatever stats you want to provide (there's no rule says that a function decorator has to return a function!). ChrisA
Thanks for the link, the rationale for rejection seems reasonable. On Fri, Dec 11, 2020 at 8:19 PM Chris Angelico <rosuav@gmail.com> wrote:
On Sat, Dec 12, 2020 at 11:42 AM Jonathan Crall <erotemic@gmail.com> wrote:
I'm not sure if this has been asked / suggested before.
I'm wondering if there is any interest in conditional or loop-based
`with` statements. I think it could be done without a syntax change.
### Napkin proposal
Context managers could define `__if__` or `__for__`, and if those dunder
methods were defined, then the `with` statement would either behave like a conditional or a loop.
If `__if__` was defined then
``` with Ctx(): print('hi') ```
would only print `hi` if `__if__` returned True. This doesn't require a
syntax change.
This part has been proposed before: https://www.python.org/dev/peps/pep-0377/
The `__for__` variant would likely need a minor syntax change.
``` with item in Ctx(): print(item) ```
The `__for__` method is a generator that generates arguments of a loop. The item will be printed as many times as there are items generated by `__for__`.
Not sure that this one has, but it's basically just a context manager and a for loop, so I'm not really sure how much you'd gain over just using the two constructs independently, given that there'd then be massive confusion over "when should I use 'with item in thing' and when should I use 'for item in thing'?".
For many use cases, it may be best to write the body as a function, which can then be called more than once. You can decorate a function in order to do whatever you like, and the return value from the decorator could be whatever stats you want to provide (there's no rule says that a function decorator has to return a function!).
ChrisA _______________________________________________ Python-ideas mailing list -- python-ideas@python.org To unsubscribe send an email to python-ideas-leave@python.org https://mail.python.org/mailman3/lists/python-ideas.python.org/ Message archived at https://mail.python.org/archives/list/python-ideas@python.org/message/Q7WZ7F... Code of Conduct: http://python.org/psf/codeofconduct/
-- -Dr. Jon Crall (him)
I ran into another case where I wish I had some sort of conditional if. I was writing code to cache autogenerated demodata. import ubelt as ub from os.path import join import json kwargs = {'example': 'config'} dpath = ub.ensure_app_cache_dir('my_modname', 'demodata') fpath = join(dpath, 'data.json') stamp = ub.CacheStamp('demodata', depends=kwargs, dpath=dpath) if stamp.expired(): data = { 'complicated': 'data', 'key1': 'val1', 'key2': 'val2', 'keyN': 'valN', } with open(fpath, 'w') as file: json.dump(data, file) stamp.renew() else: with open(fpath, 'r') as file: data = json.load(file) I really wish I didn't have to have that stamp.renew() at the end of the if block. It unnecessary boilerplate --- the caching logic would all be in a contiguous block if not for the need for this. It wastes a little bit of vertical, which while not critical, is a consideration. I want to focus on the context: stamp = ub.CacheStamp('demodata', depends=kwargs, dpath=dpath) if stamp.expired(): [code] stamp.renew() I could make `CacheStamp` a context manager and tell it to do it in its `__exit__` clause, as such: stamp = ub.CacheStamp('demodata', depends=kwargs, dpath=dpath) if stamp.expired(): with stamp: [code] This removes the need for the `stamp.renew`, makes the logic contiguous, has the same vertical space, however, it adds a TON of horizontal space depending on the complexity of the logic. It's often beneficial to minimize nesting and try restricting it 2 or 3 levels. BUT if we had just a tiny bit of new syntax rules we could write something like this: stamp = ub.CacheStamp('demodata', depends=kwargs, dpath=dpath) if stamp.expired() with stamp: [code] There are even more conservative changes, like requiring a colon before the `with`: `if stamp.expired(): with stamp:` I know this is just saving a line. But I use this pattern with `ubelt.CacheStamp` and `ubelt.Cacher` frequently, and I always feel a strong want for this syntactic sugar as I'm writing it. Wasting that horizontal space is not an option, and I really would like the caching logic to be contiguous. To be clear, in the proposed syntax: if [condition] with [obj]: [code] Would behave exactly as: if [condition]: with [obj]: [code] Is there any chance that this conditional context manager syntax might be considered? Does anyone but myself think this might be a good idea?
On 2021-02-07 00:57, Jonathan Crall wrote:
[snip]
To be clear, in the proposed syntax:
if [condition] with [obj]: [code]
Would behave exactly as:
if [condition]: with [obj]: [code]
Is there any chance that this conditional context manager syntax might be considered? Does anyone but myself think this might be a good idea?
-1. It's not that much shorter. You wouldn't be saving much typing or space.
El dom, 7 feb 2021 a las 3:08, MRAB (<python@mrabarnett.plus.com>) escribió:
On 2021-02-07 00:57, Jonathan Crall wrote:
[snip]
To be clear, in the proposed syntax:
if [condition] with [obj]: [code]
Would behave exactly as:
if [condition]: with [obj]: [code]
Is there any chance that this conditional context manager syntax might be considered? Does anyone but myself think this might be a good idea?
-1. It's not that much shorter. You wouldn't be saving much typing or space.
TL;DR: -1 for other reasons Space saved is similar to (really not necesary, but very convenient) `elif`: 4 spaces by line. My doubts are not by space saved, instead are for convenience. Nested ifs are very common contruction and there are not much to thing about. I doubt than `with` after `if` and with same scope that `if` isn't a very improbable structure. Other thing strange about proposal is: why only with after if? What about `else` (in `if`, `for`, `while`), `elif`, `for`, `while`? And in `try`, `except`, `else`, `finally`? When is coherent an mixed `with` and when not? As programmer I wan't think too much about language, so I need consistence to get focused in resolve problems with it. If I see uncommon `with` after `if` with same scope to be convenient, the other constructions needed to coherence and consistence in language seems very very strange to need (or even think in) syntactic sugar. regards, Javi
Hello, On Sat, 6 Feb 2021 19:57:33 -0500 Jonathan Crall <erotemic@gmail.com> wrote:
I ran into another case where I wish I had some sort of conditional if.
Right, conditional "if" is exactly what we miss in Python. Yes, it's a typo, but it's proverbial Freudian slip, shows what stays behind such proposals - un[spell]checked desire to add some tautology to the language.
Wasting that horizontal space is not an option
Reading things like this, it seems that some people just can't get at peace with Python's indentation-based syntax, and would jump in no time to an alternative syntax which allows to smack indentation. It was also said many times, that it you want some trivial variety on the existing language, pick up your favorite macro package and go ahead, coding trivial macros in trivial. -- Best regards, Paul mailto:pmiscml@gmail.com
@MRAB
It's not that much shorter. You wouldn't be saving much typing or space.
IMO, that's a weak point. The amount of indentation you need doubles, and that is significant. Also, common style guides and best practices recommend no more than 80 chars per line. Losing 5.5% of my available space when I'm already in a method per line is not nothing. With modern editors there is no typing cost to either method. I never claimed this would save typing. But it does clearly save a lot of horizontal space (and 1 vertical line, which is not nothing). @Paul Sokolovsky
but it's proverbial Freudian slip ...
Yes, I made a typo. Try not to judge too harshly because of a dyslexic failure to catch a grammatical issue.
it seems that some people just can't get at peace with Python's indentation-based syntax,
And I'm all on board with indentation based syntax. I'm not typically one to try and make Python work like other languages. My motivation simply stems from the fact that I've been bothered how a with directly after an if costs me 8 spaces instead of 4. I strongly feel like this would be a good change, and I am going to fight at least a little bit to defend the idea, or at least try to advocate for it and help refine the syntax into something better. I do think this new syntax is a lot better than my original proposal, which is admittedly not a good idea, It's possible this could be tweaked more to improve it. Indentation makes beautiful code, but it's not free. The more horizontal space you take --- the more nesting in your code --- the higher its complexity. I don't feel that having a with statement immediately after an if (or a for loop for that matter) is adding much complexity, but if forces double indentation of whatever code was going to be after the for loop. @Iasizoillo
Other thing strange about proposal is: why only with after if?
However isn't the fact that you *can* do if 1: return 1 also weird? I think something like `if [condition] for x in [iterable]` would be reasonable. I also suppose `if [condition] while [other-condtion]` have some use as well. I can also see uses for something like `for var in [iterable] with var:` being useful. I can actually think of a specific case where I would use it. Currently my timerit package requires syntax like: import timerit for timer in timerit.Timerit(num=100, bestof=3, verbose=1): with timer: [code] But that could be simplified to import timerit for timer in timerit.Timerit(num=100, bestof=3, verbose=1) with timer: [code] And free up one level of indentation (very useful if you are trying to keep under 80 chars per line) Perhaps the new syntax rule is as simple as, if a keyword that would normally require a new scope is placed where the colon would be for another statement that defines a new scope, then any code indented afterwords is treated as in the scope of all of the statements. That means you could do something crazy like: if foo if bar or baz with biz if buz: [code] In that example the biz context manager would only trigger if `foo and (bar or baz)` and if it did and buz was true then [code] would execute. Obviously I wouldn't recommend doing something like this stylistically, it would be a "bad code" usage of the proposed syntax (but all syntax has ways that it can be used to write bad code). This example does make me lean slightly more in favor of requiring the colon before the next "scoping" keyword is used, e.g.: if foo: if bar or baz: with biz: if buz: [code] Note that by convention an `else` would need to correspond to the outermost (or alternatively innermost) scoping keyword, so for if foo: if bar or baz: with biz: if buz: [code1] else: [code2] [code2] only executes if `not foo`. On Sun, Feb 7, 2021 at 1:28 AM Paul Sokolovsky <pmiscml@gmail.com> wrote:
Hello,
On Sat, 6 Feb 2021 19:57:33 -0500 Jonathan Crall <erotemic@gmail.com> wrote:
I ran into another case where I wish I had some sort of conditional if.
Right, conditional "if" is exactly what we miss in Python.
Yes, it's a typo, but it's proverbial Freudian slip, shows what stays behind such proposals - un[spell]checked desire to add some tautology to the language.
Wasting that horizontal space is not an option
Reading things like this, it seems that some people just can't get at peace with Python's indentation-based syntax, and would jump in no time to an alternative syntax which allows to smack indentation.
It was also said many times, that it you want some trivial variety on the existing language, pick up your favorite macro package and go ahead, coding trivial macros in trivial.
-- Best regards, Paul mailto:pmiscml@gmail.com
-- -Dr. Jon Crall (him)
El dom, 7 feb 2021 a las 9:38, Jonathan Crall (<erotemic@gmail.com>) escribió:
Currently my timerit package requires syntax like:
import timerit for timer in timerit.Timerit(num=100, bestof=3, verbose=1): with timer: [code]
But that could be simplified to
import timerit for timer in timerit.Timerit(num=100, bestof=3, verbose=1) with timer: [code]
Only if you don't setup vars like oficial example* or not calc some statistic after timered block * https://timerit.readthedocs.io/en/latest/#timerit-documentation
And free up one level of indentation (very useful if you are trying to keep under 80 chars per line)
In your example this is true by luck: import timerit class MyClass: def myfunc(): # Next line is 79 chars long, at limit of restricted pep8 for timer in timerit.Timerit(num=100, bestof=3, verbose=1) with timer: [code] I usually have identation "problems" in ifs, sometimes in for clauses. I usually have not problems in function bodys. I dislike compacted form because: - i doubt that is good comply their objetive: reduce line length in body but extend it in control flow lines. - It's less readable for me. I need to attend to long lines to determine control flow. - Refactors are complicated. Think in add a initialization block in your example and how many lines are changed to be reviewed by your peers. - It's syntactic sugar for only some marginal edge cases: it cloying the language Anyway pep8** says: Some teams strongly prefer a longer line length. For code maintained exclusively or primarily by a team that can reach agreement on this issue, *it is okay to increase the line length limit up to 99 characters*, provided that comments and docstrings are still wrapped at 72 characters. ** https://www.python.org/dev/peps/pep-0008/#maximum-line-length So, if 79 chars by line it's too hard for your team you can agree 99 chars lines with your team. Maybe you find problems in: - web documentation that includes fixed designs for 79 chars (usually you can use other theme) - errors in linters (can be configured to new line width) - forcing horizontal scroll in 3ways merges for some team members ¯\_(ツ)_/¯ rarely you find problems by terminal widths (this is a very ancient problem resolved in our century). So I recomend you relaxed pep8 with 99 chars limit by line in your code instead to pretend change syntax with no clear gains. regards, Javi
On Sun, Feb 7, 2021 at 12:37 AM Jonathan Crall <erotemic@gmail.com> wrote:
a with directly after an if costs me 8 spaces instead of 4.\
Frankly, I think many (most) uses of with are a waste of an indentation level. For example, if all I'm doing is opening a file -- that isn't "logically" a separate block. In fact, I find myself reading entire files into memory and then processing them, so as to avoid having to extra-indent all the processing code. The point being that the problem is not with "if" not having a feature, but with with (heh, heh) itself in many contexts. All that being said, it's not that big a deal, and I personally don't try to limit to 80 chars per line anyway -- preferring 90 or 95 -- I haven't used a VT100 in decades .... It's the price we pay for nifty feature. -Chris B PS -- your linter will yell at you, but you COULD simply use 2 spaces for a if:with combo. -- Christopher Barker, PhD (Chris) Python Language Consulting - Teaching - Scientific Software Development - Desktop GUI and Web Development - wxPython, numpy, scipy, Cython
On 8/02/21 6:59 am, Christopher Barker wrote:
I find myself reading entire files into memory and then processing them, so as to avoid having to extra-indent all the processing code.
I don't know why some people seem to be so afraid of indentation levels. Remember, you don't *have* to indent by 4 spaces. I often write Python with 2-space indents, which is quite readable and gives me twice as many indentation levels to play with. -- Greg
On Sun, 7 Feb 2021 at 23:55, Greg Ewing <greg.ewing@canterbury.ac.nz> wrote:
On 8/02/21 6:59 am, Christopher Barker wrote:
I find myself reading entire files into memory and then processing them, so as to avoid having to extra-indent all the processing code.
I don't know why some people seem to be so afraid of indentation levels. Remember, you don't *have* to indent by 4 spaces. I often write Python with 2-space indents, which is quite readable and gives me twice as many indentation levels to play with.
My preferred option is to use functions. I would rather not have too many levels of indentation regardless of how much actual horizontal space is involved. -- Oscar
Oscar Benjamin writes:
On Sun, 7 Feb 2021 at 23:55, Greg Ewing <greg.ewing@canterbury.ac.nz> wrote:
On 8/02/21 6:59 am, Christopher Barker wrote:
I find myself I often write My preferred option is
There's ALWAYS more than one way to do it! :-) I'm not a fan of the proposed new syntax. A big issue for me is that "with" doesn't commute with "if". That is Chris will write with open('file') as f: lines = r.readlines() for line in lines: process(line) instead of with open('file') as f: for line in f: process(line) but AFAICS the proposed syntax doesn't help by itself: for line in (what?) with open('file'): process(line) and for line in f with open('file') as f: process(line) will be horrible with any more complicated context manager constructor IMO, YMMV. Note that I'm assuming a similar facility for 'for', for convenience of exposition. Nobody has proposed it yet, I think, just mentioned it. I guess you could do as Jonathan's code does: f = open('file', and, many, more, arguments) for line in f with f: process(line) but I'm not impressed by that syntax. In the case in Jonathan's post, if stamp.expired(): with stamp: [code] is TRT, but I wonder how often it is, compared to cases where with stamp: if stamp.expired(): [code] DTRTs. Notice that once you combine them into a single statement, you can choose either semantics, which is a bug magnet. Of course you could try allowing both "if ... with ..." and "with ... if ..." with the evident semantics, but "with thing if" already has a meaning ("thing if" introduces a conditional expression). Although the PEG parser can presumably handle it, I'm not a fan of parsing "with thing if" as a combined with/if statement if the else arm is missing, and otherwise as a with/conditional expression statement. Bottom line: IMO this syntax probably should be limited to the particular case where there's a context manager that doesn't need to be referred to in the block, and the nesting is if = outer, with = inner. I don't think that's enough to justify new syntax, merely to save one level of indentation that can be saved in a large number of ways that probably cover most of the use cases. Steve
On 2021-02-07 09:59, Christopher Barker wrote:
All that being said, it's not that big a deal, and I personally don't try to limit to 80 chars per line anyway -- preferring 90 or 95 -- I haven't used a VT100 in decades ....
To be honest I find it kind of ridiculous that people are still worrying about line lengths in this day and age. It's especially strange that people will talk about how a proposed feature interacts with relatively sophisticated editor/IDE features like syntax highlighting and autocompletion, yet still have no problem working with editors that are woefully limited on a much more basic feature. It's called line wrapping. We as humans should not be concerned with how long LOGICAL lines are until and unless it actually impinges on our ability to comprehend them. The appearance of the VISUAL line should not enter into our decision-making because that's a matter for the editor displaying the code. There's no reason not have lines that are 200 or 300 or even 1000 characters long if you want to (for instance for a long string literal); it's the editor's job to take a semantically-motivated set of lines and indentations and display them attractively. Then if different people have different preferences they can set their editors to different line widths and see the code in their preferred format. Editors that don't support such features need to be fixed and we shouldn't continue to enable them by insisting that people distort the semantic structure of their code to fit arbitrary guidelines like 80 or 95 characters. A person should insert a line break whenever they think it's a good idea for semantic reasons (e.g., separating statements) and not insert any when they don't think so, and leave the rest up to the editor. -- Brendan Barnwell "Do not follow where the path may lead. Go, instead, where there is no path, and leave a trail." --author unknown
Code is READ far more often than it is written! Lines more than 80-ish characters impose a rapidly increasing cognitive and visual burden with every additional character. Really, starting at more like 70 characters. It's not quite exponential in the harm, but it's strongly super-linear, after the threshold. I use a 32" high res screen and run my terminal in full screen mode. Even with eyes not quite so sharp as when I was younger, I can easily read about 300 characters wide in a very legible font.[*] I ALMOST NEVER write lines that exceed 80 characters. If I am project lead or technical manager I will first warn anyone who does, then fire them as harmful to the project if they don't fix their habits. [*] Although my terminal is that big, I use tmux to have several panes of useful width. The widest, central, one is something like 112 chars, but I really don't want the rightmost part to show code EVER. On Sun, Feb 7, 2021, 8:20 PM Brendan Barnwell <brenbarn@brenbarn.net> wrote:
On 2021-02-07 09:59, Christopher Barker wrote:
All that being said, it's not that big a deal, and I personally don't try to limit to 80 chars per line anyway -- preferring 90 or 95 -- I haven't used a VT100 in decades ....
To be honest I find it kind of ridiculous that people are still worrying about line lengths in this day and age. It's especially strange that people will talk about how a proposed feature interacts with relatively sophisticated editor/IDE features like syntax highlighting and autocompletion, yet still have no problem working with editors that are woefully limited on a much more basic feature. It's called line wrapping.
We as humans should not be concerned with how long LOGICAL lines are until and unless it actually impinges on our ability to comprehend them. The appearance of the VISUAL line should not enter into our decision-making because that's a matter for the editor displaying the code. There's no reason not have lines that are 200 or 300 or even 1000 characters long if you want to (for instance for a long string literal); it's the editor's job to take a semantically-motivated set of lines and indentations and display them attractively. Then if different people have different preferences they can set their editors to different line widths and see the code in their preferred format. Editors that don't support such features need to be fixed and we shouldn't continue to enable them by insisting that people distort the semantic structure of their code to fit arbitrary guidelines like 80 or 95 characters. A person should insert a line break whenever they think it's a good idea for semantic reasons (e.g., separating statements) and not insert any when they don't think so, and leave the rest up to the editor.
-- Brendan Barnwell "Do not follow where the path may lead. Go, instead, where there is no path, and leave a trail." --author unknown _______________________________________________ Python-ideas mailing list -- python-ideas@python.org To unsubscribe send an email to python-ideas-leave@python.org https://mail.python.org/mailman3/lists/python-ideas.python.org/ Message archived at https://mail.python.org/archives/list/python-ideas@python.org/message/UBDGX2... Code of Conduct: http://python.org/psf/codeofconduct/
There's a reason that never in the last 3800 years since Proto-Sinaitic was the first human script to approximately represent phonemes, has text EVER been set at more than 80 characters as a widespread convention. Large print sizes have existed for a long time, such as newspapers and broadsheets. Always and everywhere, in every human script, these limit column widths to something around 65 characters. They do this in Roman alphabets, in Greek, in Cyrillic, in Ethiopic, in Devanagari, in Armenian, in Georgian. Even Hangul which uses composed syllable blocks follows this. There are very few human universals. Line widths being normatively less than 80 characters is one of them. More than shared ethics, or diet, or religion, or social structures. The truest thing one can say about the human species is that our cognition works far better broken into units of fewer than 80 consecutive graphemes. On Mon, Feb 8, 2021, 1:36 AM David Mertz <mertz@gnosis.cx> wrote:
Code is READ far more often than it is written!
Lines more than 80-ish characters impose a rapidly increasing cognitive and visual burden with every additional character. Really, starting at more like 70 characters. It's not quite exponential in the harm, but it's strongly super-linear, after the threshold.
I use a 32" high res screen and run my terminal in full screen mode. Even with eyes not quite so sharp as when I was younger, I can easily read about 300 characters wide in a very legible font.[*]
I ALMOST NEVER write lines that exceed 80 characters. If I am project lead or technical manager I will first warn anyone who does, then fire them as harmful to the project if they don't fix their habits.
[*] Although my terminal is that big, I use tmux to have several panes of useful width. The widest, central, one is something like 112 chars, but I really don't want the rightmost part to show code EVER.
On Sun, Feb 7, 2021, 8:20 PM Brendan Barnwell <brenbarn@brenbarn.net> wrote:
On 2021-02-07 09:59, Christopher Barker wrote:
All that being said, it's not that big a deal, and I personally don't try to limit to 80 chars per line anyway -- preferring 90 or 95 -- I haven't used a VT100 in decades ....
To be honest I find it kind of ridiculous that people are still worrying about line lengths in this day and age. It's especially strange that people will talk about how a proposed feature interacts with relatively sophisticated editor/IDE features like syntax highlighting and autocompletion, yet still have no problem working with editors that are woefully limited on a much more basic feature. It's called line wrapping.
We as humans should not be concerned with how long LOGICAL lines are until and unless it actually impinges on our ability to comprehend them. The appearance of the VISUAL line should not enter into our decision-making because that's a matter for the editor displaying the code. There's no reason not have lines that are 200 or 300 or even 1000 characters long if you want to (for instance for a long string literal); it's the editor's job to take a semantically-motivated set of lines and indentations and display them attractively. Then if different people have different preferences they can set their editors to different line widths and see the code in their preferred format. Editors that don't support such features need to be fixed and we shouldn't continue to enable them by insisting that people distort the semantic structure of their code to fit arbitrary guidelines like 80 or 95 characters. A person should insert a line break whenever they think it's a good idea for semantic reasons (e.g., separating statements) and not insert any when they don't think so, and leave the rest up to the editor.
-- Brendan Barnwell "Do not follow where the path may lead. Go, instead, where there is no path, and leave a trail." --author unknown _______________________________________________ Python-ideas mailing list -- python-ideas@python.org To unsubscribe send an email to python-ideas-leave@python.org https://mail.python.org/mailman3/lists/python-ideas.python.org/ Message archived at https://mail.python.org/archives/list/python-ideas@python.org/message/UBDGX2... Code of Conduct: http://python.org/psf/codeofconduct/
On Mon, Feb 8, 2021 at 6:04 PM David Mertz <mertz@gnosis.cx> wrote:
There's a reason that never in the last 3800 years since Proto-Sinaitic was the first human script to approximately represent phonemes, has text EVER been set at more than 80 characters as a widespread convention.
What did they use as a tab width though? ChrisA
bringing this back (closer to) on lopic: Are you suggesting that we add syntax to Python that makes it easier to keep the number of levels of indentation down? Because when you have a method in a class, you've already lost 1/10 or your line. then you have a for, and an if, and a with, and you've used up 5/8 of the line. However, if you are talking about the readability of and individual not-so long line, then I'd say you can ignore the 8 spaces that got you into the method, and we're now at a 88 char line :-) -Chris B On Sun, Feb 7, 2021 at 11:09 PM Chris Angelico <rosuav@gmail.com> wrote:
On Mon, Feb 8, 2021 at 6:04 PM David Mertz <mertz@gnosis.cx> wrote:
There's a reason that never in the last 3800 years since Proto-Sinaitic
was the first human script to approximately represent phonemes, has text EVER been set at more than 80 characters as a widespread convention.
What did they use as a tab width though?
ChrisA _______________________________________________ Python-ideas mailing list -- python-ideas@python.org To unsubscribe send an email to python-ideas-leave@python.org https://mail.python.org/mailman3/lists/python-ideas.python.org/ Message archived at https://mail.python.org/archives/list/python-ideas@python.org/message/2KGKLH... Code of Conduct: http://python.org/psf/codeofconduct/
-- Christopher Barker, PhD (Chris) Python Language Consulting - Teaching - Scientific Software Development - Desktop GUI and Web Development - wxPython, numpy, scipy, Cython
I don't like the actual proposal. It weirdly combines almost orthogonal concepts into the same block statement. Obviously matters other than indent level are important too (notwithstanding my sincere digression into my bête noire). Nested blocks are an excellent way to express roughly orthogonal ideas. On Mon, Feb 8, 2021, 2:18 AM Christopher Barker <pythonchb@gmail.com> wrote:
bringing this back (closer to) on lopic:
Are you suggesting that we add syntax to Python that makes it easier to keep the number of levels of indentation down?
Because when you have a method in a class, you've already lost 1/10 or your line. then you have a for, and an if, and a with, and you've used up 5/8 of the line.
However, if you are talking about the readability of and individual not-so long line, then I'd say you can ignore the 8 spaces that got you into the method, and we're now at a 88 char line :-)
-Chris B
On Sun, Feb 7, 2021 at 11:09 PM Chris Angelico <rosuav@gmail.com> wrote:
On Mon, Feb 8, 2021 at 6:04 PM David Mertz <mertz@gnosis.cx> wrote:
There's a reason that never in the last 3800 years since Proto-Sinaitic
was the first human script to approximately represent phonemes, has text EVER been set at more than 80 characters as a widespread convention.
What did they use as a tab width though?
ChrisA _______________________________________________ Python-ideas mailing list -- python-ideas@python.org To unsubscribe send an email to python-ideas-leave@python.org https://mail.python.org/mailman3/lists/python-ideas.python.org/ Message archived at https://mail.python.org/archives/list/python-ideas@python.org/message/2KGKLH... Code of Conduct: http://python.org/psf/codeofconduct/
-- Christopher Barker, PhD (Chris)
Python Language Consulting - Teaching - Scientific Software Development - Desktop GUI and Web Development - wxPython, numpy, scipy, Cython _______________________________________________ Python-ideas mailing list -- python-ideas@python.org To unsubscribe send an email to python-ideas-leave@python.org https://mail.python.org/mailman3/lists/python-ideas.python.org/ Message archived at https://mail.python.org/archives/list/python-ideas@python.org/message/GZY32Q... Code of Conduct: http://python.org/psf/codeofconduct/
On Mon, Feb 08, 2021 at 02:45:57AM -0500, David Mertz wrote:
I don't like the actual proposal. It weirdly combines almost orthogonal concepts into the same block statement.
I'm curious why you say that if and with are "almost" orthogonal concepts? They seem completely orthogonal to me. You can mix and match them in literally every combination: * if alone * with alone * if block inside the with block * with block inside the if block * with block inside the else block etc. Why single out one special case? But generalising this is worse. We don't want Perlish one liners. # where the process ends if condition: for x in items: with thingy as cm: if predicate: while cond: for y in stuff: do_something() -- Steve
On Sun, Feb 14, 2021, 5:46 PM Steven D'Aprano <steve@pearwood.info> wrote:
I'm curious why you say that if and with are "almost" orthogonal concepts? They seem completely orthogonal to me. You can mix and match them in literally every combination:
I mean in a conceptual sense, not as a matter of Python grammar. Grammatically, I can nest 'for' and 'while' inside each other in any combination, but they do "kinda the same thing." I genuinely need to decide sometimes whether to use one or the other for greater clarity in a certain bit of code. I never need to make that decision between 'if' and 'with'. Python wouldn't be that much worse a language if it dropped one of 'for' and 'while'. Of course I don't advocate such, but it's easy to write: for _ in itertools.repeat(None): if something: break Or alternatively, while True: loopvar = next(stuff) The only sense in which I grant "almost" to with/if is that *occasionally* I want to perform an if test on a context object right after it's bound. In that uncommon case, they are slightly non-orthogonal. But the same is true of for/if and other combos.
From what I can tell, zero-width tabs. Terrible script to write Python in... But fine for the compacted JavaScript sent by most servers. On Mon, Feb 8, 2021, 2:07 AM Chris Angelico <rosuav@gmail.com> wrote:
On Mon, Feb 8, 2021 at 6:04 PM David Mertz <mertz@gnosis.cx> wrote:
There's a reason that never in the last 3800 years since Proto-Sinaitic
was the first human script to approximately represent phonemes, has text EVER been set at more than 80 characters as a widespread convention.
What did they use as a tab width though?
ChrisA _______________________________________________ Python-ideas mailing list -- python-ideas@python.org To unsubscribe send an email to python-ideas-leave@python.org https://mail.python.org/mailman3/lists/python-ideas.python.org/ Message archived at https://mail.python.org/archives/list/python-ideas@python.org/message/2KGKLH... Code of Conduct: http://python.org/psf/codeofconduct/
On 2021-02-07 22:36, David Mertz wrote:
Code is READ far more often than it is written!
Lines more than 80-ish characters impose a rapidly increasing cognitive and visual burden with every additional character. Really, starting at more like 70 characters. It's not quite exponential in the harm, but it's strongly super-linear, after the threshold.
Setting aside whether this really applies to something like code, where lots of the line is whitespace, what you're talking about here is the visual width of the line on the screen. I agree that that can be important. What I'm saying is that there is no reason for that to constrain the logical length of the number of characters before a line break character. It's precisely because code is read more often than it's written that we should not, at the time of writing it, make these kinds of needlessly detailed decisions about how it's going to look. How it looks should be up to the person reading it, and the best way to do that is to include line breaks in semantically meaningful places and let the editor (as configured by the reader) choose how to map that onto a visual display. -- Brendan Barnwell "Do not follow where the path may lead. Go, instead, where there is no path, and leave a trail." --author unknown
On Mon, Feb 8, 2021, 2:15 AM Brendan Barnwell <brenbarn@brenbarn.net> wrote:
On 2021-02-07 22:36, David Mertz wrote:
Code is READ far more often than it is written! Lines more than 80-ish characters impose a rapidly increasing cognitive and visual burden with every additional character. Really, starting at more like 70 characters. It's not quite exponential in the harm, but it's strongly super-linear, after the threshold.
What I'm saying is that there is no reason for that to constrain the logical length of the number of characters before a line break character. It's precisely because code is read more often than it's written that we should not, at the time of writing it, make these kinds of needlessly detailed decisions about how it's going to look.
When you write code, you should try to communicate to HUMANS even more than to computers. Computers don't care about comments, for example (for most purposes). But humans do. You should use neither too few nor too many, and those you choose should convey what is most important to communicate, above and beyond the code syntax that humans can also understand. A line of text, in human perception, makes up a unit of understanding. It's not the only unit. Sentences do not usually end where lines do, in prose. A "logical line" of code in Python or other languages may need more than 80 characters. Python is better here than languages that tends to use very long identifiers, for example. But it is an important service to your readers to break lines at "clauses" that aid their focus and organize the small cognitive breaks between physical lines. You know about parens and trailing backslashes just as well as I do, and about refactoring; there are many ways to fulfill your ethical duties towards readers. A code editor, or even a smart formatter like black, simply cannot make those decisions as conscientiously as a human programmer can (if the latter feels compunctions). Yes, the tool can use fairly sophisticated heuristics, but you have an obligation to treat your readers not just "pretty well" but rather the best you possibly can.
Brendan Barnwell writes:
let the editor (as configured by the reader) choose how to map that [long logical line] onto a visual display.
I think your editor must be the first example of true intelligence in a machine. I don't know any editors that reliably do this readably, let alone to my taste.
@Iasizoillo
Only if you don't setup vars like official example* or not calc some statistic after timered block
I wrote the official docs, so I'm aware of that case. In the case where setup variables are required it make sense to me to have an extra level of indentation. It just bothers me that I still need extra indentation when I *don't* need setup variables.
In your example this is true by luck,
Yes, but I also chose a verbose and explicit invocation of `Timerit` because people may not be familiar with what the default arguments are. I'm also not opposed to line wrapping e.g. import timerit class MyClass: def myfunc(): # Next line is 79 chars long, at limit of restricted pep8 for timer in timerit.Timerit( num=100, bestof=3, verbose=1): with timer: [code] Although, at this point I would have refactored the code to look like this: import timerit class MyClass: def myfunc(): ti = timerit.Timerit(num=100, bestof=3, verbose=1) for timer in ti: with timer: [code] Furthermore, having one line that is over the character limit (whether it be 79, 99, or whatever), is a lot easier to deal with than multiple lines in the [code] block. @Greg Ewing
I don't know why some people seem to be so afraid of indentation levels
There are a few reasons for me. I don't like when my linter yells at me. But more importantly: I don't like when lines wrap around (or I have to horizontally scroll) when I have two files open on a portrait monitor in vertical split mode and lines wrap around. On my 4K setup, I have 97 characters of horizontal space to play with in the best case (it's worse if I have a NerdTree sidebar open). On my older monitor this situation is worse.
Remember, you don't *have* to indent by 4 spaces. I often write Python with 2-space indents, which is quite readable and gives me twice as many indentation levels to play with.
This is a good point, and one I have considered. I know I *could*, and I don't have a great argument against this point. It feels wrong though. Perhaps in the same way that `if [cond]: with [obj]:` feels wrong to others? Or maybe its the more grounded facts that (1) I prefer a consistent indentation level and (2) I set my editor to use a 4 space indentation level and mixing and matching 2 or 4 space indentations mess with that. I do think this is one of the best arguments against my idea, but at the same time I feel like the idea has enough merit where this argument against it doesn't kill it. @Stephen J Turnbull
A big issue for me is that "with" doesn't commute with "if".
That would be a huge problem if "if" commuted with "with"! The order of nesting *should* be the order of appearance. Your example: for line in f with open('file') as f: process(line) Should be rewritten as: with open('file') as f: for line in f: process(line) The other way around, the with would happen in every iteration of the loop.
Notice that once you combine them into a single statement, you can choose either semantics, which is a bug magnet
IMO this syntax probably should be limited to the particular case where
To be fair, coding is a bug magnet. And yes, there would need to be a rule that defines semantics. Again, I would propose that "the order of appearance is the order of nesting" should be that rule. there's a context manager Having considered this more, I prefer the style where the grammar effectively allows chaining of nesting statements. It seems a lot more elegant and consistent than constraining this to just conditionals. If implemented, I personally would likely only use this for the context manager case, but I know people like Jeremy Howard would have a field day with it. @David Mertz
Code is READ far more often than it is written!
Yes! I find myself reading my double indented context blocks / non-contiguous caching logic a lot and wishing they were smaller / contiguous.
I don't like the actual proposal. It weirdly combines almost orthogonal concepts into the same block statement.
This is a good point. I think the syntax -- as with any other programming construct -- should be used judiciously. I envision it used in cases like the ones I mentioned where the `if` and `with` deal with correlated information. `if stamp.expired():` and `with stamp:` are not orthogonal. @Christopher Barker
Are you suggesting that we add syntax to Python that makes it easier to keep the number of levels of indentation down?
Yes, I think the core essence of this proposal is a way to keep indentation down. Especially in cases where there are multiple levels of (short) nesting statements with no other logic in between. This is a fairly common case, and having the option to reduce required indentation seems like it might be nice. Obviously this isn't critical for the Python grammar, but at this point what is? It's such a beautiful language already! Almost anything added now is going to be for convenience (e.g. format strings).
However, if you are talking about the readability of and individual not-so long line, then I'd say you can ignore the 8 spaces that got you into the method, and we're now at a 88 char line :-)
While many modern monitors can deal with this, I often run into cases (especially when working with split panes) where horizontal space is still limited. As an analogy, just because we have 12 TB hard drives, doesn't mean we no longer need to care about file compression. On Mon, Feb 8, 2021 at 3:50 AM Stephen J. Turnbull < turnbull.stephen.fw@u.tsukuba.ac.jp> wrote:
Brendan Barnwell writes:
let the editor (as configured by the reader) choose how to map that [long logical line] onto a visual display.
I think your editor must be the first example of true intelligence in a machine. I don't know any editors that reliably do this readably, let alone to my taste. _______________________________________________ Python-ideas mailing list -- python-ideas@python.org To unsubscribe send an email to python-ideas-leave@python.org https://mail.python.org/mailman3/lists/python-ideas.python.org/ Message archived at https://mail.python.org/archives/list/python-ideas@python.org/message/BMAMYL... Code of Conduct: http://python.org/psf/codeofconduct/
-- -Dr. Jon Crall (him)
On 08/02/2021 00:39, Brendan Barnwell wrote:
On 2021-02-07 09:59, Christopher Barker wrote:
All that being said, it's not that big a deal, and I personally don't try to limit to 80 chars per line anyway -- preferring 90 or 95 -- I haven't used a VT100 in decades ....
To be honest I find it kind of ridiculous that people are still worrying about line lengths in this day and age. It's especially strange that people will talk about how a proposed feature interacts with relatively sophisticated editor/IDE features like syntax highlighting and autocompletion, yet still have no problem working with editors that are woefully limited on a much more basic feature. It's called line wrapping.
We as humans should not be concerned with how long LOGICAL lines are until and unless it actually impinges on our ability to comprehend them. The appearance of the VISUAL line should not enter into our decision-making because that's a matter for the editor displaying the code. There's no reason not have lines that are 200 or 300 or even 1000 characters long if you want to (for instance for a long string literal); it's the editor's job to take a semantically-motivated set of lines and indentations and display them attractively. Then if different people have different preferences they can set their editors to different line widths and see the code in their preferred format. Editors that don't support such features need to be fixed and we shouldn't continue to enable them by insisting that people distort the semantic structure of their code to fit arbitrary guidelines like 80 or 95 characters. A person should insert a line break whenever they think it's a good idea for semantic reasons (e.g., separating statements) and not insert any when they don't think so, and leave the rest up to the editor.
+1. Keeping lines to 80 characters makes it easier to understand *individual lines*. But allowing fewer, longer lines makes it easier to perceive *the code structure as a whole*. I want to illustrate this with a bit of actual code from one of my programs. It uses lines up to 102 characters including 12-space indentation. Oh, and it breaks another Python shibboleth - not having multiple statements on one line. Apologies, it looks horrible in an e-mail with the spacing all mucked up, so I have put it in an attachment (a plain ASCII file). IMHO this is a piece of well-written, self-documenting code that I would find much harder to grok or look things up in if were laid out differently. YMMV . In fact if it were laid out differently, I would want to document it, and the documentation would be a table that looks very much like the code "as is". Rob Cliffe
Thanks Rob. You've broken a number of "rules" code code formatting there ;-) A big one is aligning things vertically, which, as a rule, I like. but this example does really push things out away from each other, so I'm not so sure. I found that the original looked like heck when I first opened it in my editor -- once I made the window wide enough it was better, but still pretty stretched out. Just for fun, here it is after being run through Black (which I don't usually use ....) -- but I think it looks better that way. Also -- my first thought looking at that was that it could really benefit from the pattern matching :-) And my second thought was that there could be a whole other pattern used here -- I usually find long chained elifs can be better expressed as a dict selection, or subclassing, or ... But not seeing the rest of the application, maybe maybe not in this case. if ch == "B": self.ToggleBackgroundColour(event) elif ch == "C": self.ChangeCommonChars(event) return elif ch == "D" and len(Areas) > 1: self.DeleteArea(event) return elif ch == "F" and __debug__: self.Info(event) return elif ch == "I": self.ChangeImagesToScroll(event) return elif ch == "J" and self.NUMBER_OF_IMAGES == 1: self.ChangeJustification(event) return elif ch == "L": self.ToggleShowLabels(event) elif ch == "M" and MaxNumberOfImages > 1: self.ChangeNumberOfImages(event) elif ch == "N" and CanZapImage: self.Rename(event) elif ch == "R" and not (area.Dynamic | area.IsTour): self.ToggleRandom(event, 1) elif ch == "S": self.ToggleSets(event, 1) elif ch == "T": self.ChangeTimer(event) return elif ch == "U" and not (area.Dynamic | area.Random): self.ToggleIsTour(event, 1) elif ch == "V" and OnAnImage: self.WindowsPhotoViewer(event) return elif ch == "W": self.ToggleWrap(event, 1) elif ch == "Y" and not (area.Random | area.IsTour): self.ToggleDynamic(event, 1) elif ch == "Z" and CanZapImage: self.Zap(event) elif ch == "A" and not (area.Dynamic | area.IsTour): self.ToggleRandom(event, 1) # Alt-R doesn't always work for some reason, so we give Alt-A as an alternative else: event.Skip() On Fri, Feb 12, 2021 at 1:46 AM Rob Cliffe via Python-ideas < python-ideas@python.org> wrote:
On 08/02/2021 00:39, Brendan Barnwell wrote:
On 2021-02-07 09:59, Christopher Barker wrote:
All that being said, it's not that big a deal, and I personally don't try to limit to 80 chars per line anyway -- preferring 90 or 95 -- I haven't used a VT100 in decades ....
To be honest I find it kind of ridiculous that people are still worrying about line lengths in this day and age. It's especially strange that people will talk about how a proposed feature interacts with relatively sophisticated editor/IDE features like syntax highlighting and autocompletion, yet still have no problem working with editors that are woefully limited on a much more basic feature. It's called line wrapping.
We as humans should not be concerned with how long LOGICAL lines are until and unless it actually impinges on our ability to comprehend them. The appearance of the VISUAL line should not enter into our decision-making because that's a matter for the editor displaying the code. There's no reason not have lines that are 200 or 300 or even 1000 characters long if you want to (for instance for a long string literal); it's the editor's job to take a semantically-motivated set of lines and indentations and display them attractively. Then if different people have different preferences they can set their editors to different line widths and see the code in their preferred format. Editors that don't support such features need to be fixed and we shouldn't continue to enable them by insisting that people distort the semantic structure of their code to fit arbitrary guidelines like 80 or 95 characters. A person should insert a line break whenever they think it's a good idea for semantic reasons (e.g., separating statements) and not insert any when they don't think so, and leave the rest up to the editor.
+1. Keeping lines to 80 characters makes it easier to understand *individual lines*. But allowing fewer, longer lines makes it easier to perceive *the code structure as a whole*. I want to illustrate this with a bit of actual code from one of my programs. It uses lines up to 102 characters including 12-space indentation. Oh, and it breaks another Python shibboleth - not having multiple statements on one line. Apologies, it looks horrible in an e-mail with the spacing all mucked up, so I have put it in an attachment (a plain ASCII file). IMHO this is a piece of well-written, self-documenting code that I would find much harder to grok or look things up in if were laid out differently. YMMV . In fact if it were laid out differently, I would want to document it, and the documentation would be a table that looks very much like the code "as is".
Rob Cliffe
_______________________________________________ Python-ideas mailing list -- python-ideas@python.org To unsubscribe send an email to python-ideas-leave@python.org https://mail.python.org/mailman3/lists/python-ideas.python.org/ Message archived at https://mail.python.org/archives/list/python-ideas@python.org/message/JSCI22... Code of Conduct: http://python.org/psf/codeofconduct/
-- Christopher Barker, PhD (Chris) Python Language Consulting - Teaching - Scientific Software Development - Desktop GUI and Web Development - wxPython, numpy, scipy, Cython
Thanks Rob.
You've broken a number of "rules" code code formatting there ;-) Thanks for the quotation marks. Indeed, PEP 8 provides guidelines, not diktats. A big one is aligning things vertically, which, as a rule, I like. but this example does really push things out away from each other, so I'm not so sure.
I found that the original looked like heck when I first opened it in my editor -- once I made the window wide enough it was better, but still pretty stretched out. It's written to suit my editor. I'm with Brendan: restricting the whole world to 80 chars per line is like insisting the whole world program in COBOL.
Just for fun, here it is after being run through Black (which I don't usually use ....) -- but I think it looks better that way.
Also -- my first thought looking at that was that it could really benefit from the pattern matching :-) Yes IMHO it would be a slight improvement. But AFAIU pattern matching is not available yet.
And my second thought was that there could be a whole other pattern used here -- I usually find long chained elifs can be better expressed as a dict selection, or subclassing, or ... I cannot imagine how a dictionary, much less a subclass (of what)? could have any relevance here. I'd be interested if you could elucidate.
But not seeing the rest of the application, maybe maybe not in this case.
if ch == "B": self.ToggleBackgroundColour(event) elif ch == "C": self.ChangeCommonChars(event) return elif ch == "D" and len(Areas) > 1: self.DeleteArea(event) return elif ch == "F" and __debug__: self.Info(event) return elif ch == "I": self.ChangeImagesToScroll(event) return elif ch == "J" and self.NUMBER_OF_IMAGES == 1: self.ChangeJustification(event) return elif ch == "L": self.ToggleShowLabels(event) elif ch == "M" and MaxNumberOfImages > 1: self.ChangeNumberOfImages(event) elif ch == "N" and CanZapImage: self.Rename(event) elif ch == "R" and not (area.Dynamic | area.IsTour): self.ToggleRandom(event, 1) elif ch == "S": self.ToggleSets(event, 1) elif ch == "T": self.ChangeTimer(event) return elif ch == "U" and not (area.Dynamic | area.Random): self.ToggleIsTour(event, 1) elif ch == "V" and OnAnImage: self.WindowsPhotoViewer(event) return elif ch == "W": self.ToggleWrap(event, 1) elif ch == "Y" and not (area.Random | area.IsTour): self.ToggleDynamic(event, 1) elif ch == "Z" and CanZapImage: self.Zap(event) elif ch == "A" and not (area.Dynamic | area.IsTour): self.ToggleRandom(event, 1) # Alt-R doesn't always work for some reason, so we give Alt-A as an alternative else: event.Skip() I'm well aware that if you apply PEP 8 guidelines strictly it would look
On 14/02/2021 02:23, Christopher Barker wrote: like the above. But is this supposed to be an improvement? My original was a clear, tabular layout making the code structure completely clear, the vertical alignment *highlighting where different cases are treated differently and where they are treated similarly*. This is just an amorphous mass of code that I find much harder to grok. (YMMV but I find it hard to imagine.) Rules are all very well, but "A Foolish Consistency is the Hobgoblin of Little Minds" [PEP 8] Best wishes Rob PS Can anyone shed any light on why Alt-R doesn't always "work" on Windows 10? I believe there's some bit of installed software that has hijacked it, but I'm not sure what.
On Fri, Feb 12, 2021 at 1:46 AM Rob Cliffe via Python-ideas <python-ideas@python.org <mailto:python-ideas@python.org>> wrote:
On 08/02/2021 00:39, Brendan Barnwell wrote: > On 2021-02-07 09:59, Christopher Barker wrote: >> All that being said, it's not that big a deal, and I personally don't >> try to limit to 80 chars per line anyway -- preferring 90 or 95 -- I >> haven't used a VT100 in decades .... > > To be honest I find it kind of ridiculous that people are still > worrying about line lengths in this day and age. It's especially > strange that people will talk about how a proposed feature interacts > with relatively sophisticated editor/IDE features like syntax > highlighting and autocompletion, yet still have no problem working > with editors that are woefully limited on a much more basic feature. > It's called line wrapping. > > We as humans should not be concerned with how long LOGICAL lines > are until and unless it actually impinges on our ability to comprehend > them. The appearance of the VISUAL line should not enter into our > decision-making because that's a matter for the editor displaying the > code. There's no reason not have lines that are 200 or 300 or even > 1000 characters long if you want to (for instance for a long string > literal); it's the editor's job to take a semantically-motivated set > of lines and indentations and display them attractively. Then if > different people have different preferences they can set their editors > to different line widths and see the code in their preferred format. > Editors that don't support such features need to be fixed and we > shouldn't continue to enable them by insisting that people distort the > semantic structure of their code to fit arbitrary guidelines like 80 > or 95 characters. A person should insert a line break whenever they > think it's a good idea for semantic reasons (e.g., separating > statements) and not insert any when they don't think so, and leave the > rest up to the editor. > +1. Keeping lines to 80 characters makes it easier to understand *individual lines*. But allowing fewer, longer lines makes it easier to perceive *the code structure as a whole*. I want to illustrate this with a bit of actual code from one of my programs. It uses lines up to 102 characters including 12-space indentation. Oh, and it breaks another Python shibboleth - not having multiple statements on one line. Apologies, it looks horrible in an e-mail with the spacing all mucked up, so I have put it in an attachment (a plain ASCII file). IMHO this is a piece of well-written, self-documenting code that I would find much harder to grok or look things up in if were laid out differently. YMMV . In fact if it were laid out differently, I would want to document it, and the documentation would be a table that looks very much like the code "as is".
Rob Cliffe
_______________________________________________ Python-ideas mailing list -- python-ideas@python.org <mailto:python-ideas@python.org> To unsubscribe send an email to python-ideas-leave@python.org <mailto:python-ideas-leave@python.org> https://mail.python.org/mailman3/lists/python-ideas.python.org/ <https://mail.python.org/mailman3/lists/python-ideas.python.org/> Message archived at https://mail.python.org/archives/list/python-ideas@python.org/message/JSCI22... <https://mail.python.org/archives/list/python-ideas@python.org/message/JSCI223RQWSOABLTQ7OKEBQMOHJGUXBO/> Code of Conduct: http://python.org/psf/codeofconduct/ <http://python.org/psf/codeofconduct/>
-- Christopher Barker, PhD (Chris)
Python Language Consulting - Teaching - Scientific Software Development - Desktop GUI and Web Development - wxPython, numpy, scipy, Cython
Getting OT here -- you've been warned. On Sun, Feb 14, 2021 at 2:10 AM Rob Cliffe <rob.cliffe@btinternet.com> wrote:
You've broken a number of "rules" code code formatting there ;-)
Thanks for the quotation marks. Indeed, PEP 8 provides guidelines, not diktats.
A big one is aligning things vertically, which, as a rule, I like. but this example does really push things out away from each other, so I'm not so sure.
It's written to suit my editor. I'm with Brendan: restricting the whole world to 80 chars per line is like insisting the whole world program in COBOL.
I agree about the 80 char limit -- personally using usually 90 or 95. But I do think you need to consider not just your editor -- if anyone else is going to read your code. But in your example, it not only has long lines, but by lining up the stuff after the colon -- there is a LOT of info way over on the right, which I'm not so sure about.
Just for fun, here it is after being run through Black (which I don't usually use ....) -- but I think it looks better that way.
Also -- my first thought looking at that was that it could really benefit from the pattern matching :-)
Yes IMHO it would be a slight improvement. But AFAIU pattern matching is not available yet.
no, it's not. That was a note for me in a way -- I'm a bit of a skeptic about pattern matching, so this is a good example use case.
And my second thought was that there could be a whole other pattern used here -- I usually find long chained elifs can be better expressed as a dict selection, or subclassing, or ...
I cannot imagine how a dictionary, much less a subclass (of what)? could have any relevance here. I'd be interested if you could elucidate.
Again, I don't know what the rest of your code looks like, so maybe none of these would work well, but: for using a dict, you could set it up something like: ch_proc_dict = {"B": self.ToggleBackgroundColour, "C": self.ChangeCommonChars, "D": self.DeleteArea, ... } then: if process_dict[ch](event): return you also have other checks in there, so those would have to be moved into the functions in the dict, maybe with wrappers (or not -- depends on where you store some of that state data. as for subclassing, that's a very different pattern that probably doesn't apply here now that I think about it: This looks to me like wxPython code (or the like). for wx.lib.floatcanvas, I don't have keyboard accelerators, but I do have "GUI Modes" classes: Depending on what Mode is set at the moment, each event gets mapped to different actions, so you might have different handling of the character hit depending on what object is selected: object_selected.process_keystroke(ch) Of course, you'd need this kind of "switch" construct in the object class anyway. Unless you wanted to get really wordy, and have: def process_A(self..) ... def process_B(self...) ... you could be a bit saved in Python: getattr(selected_object, "process" + ch)(event) Anyway, as I say -- pretty darn off topic, but those are two of the three ways that I think "switch -- case" can be spelled in Python. -Chris B if ch == "B": self.ToggleBackgroundColour(event)
elif ch == "C": self.ChangeCommonChars(event) return elif ch == "D" and len(Areas) > 1: self.DeleteArea(event) return elif ch == "F" and __debug__: self.Info(event) return elif ch == "I": self.ChangeImagesToScroll(event) return elif ch == "J" and self.NUMBER_OF_IMAGES == 1: self.ChangeJustification(event) return elif ch == "L": self.ToggleShowLabels(event) elif ch == "M" and MaxNumberOfImages > 1: self.ChangeNumberOfImages(event) elif ch == "N" and CanZapImage: self.Rename(event) elif ch == "R" and not (area.Dynamic | area.IsTour): self.ToggleRandom(event, 1) elif ch == "S": self.ToggleSets(event, 1) elif ch == "T": self.ChangeTimer(event) return elif ch == "U" and not (area.Dynamic | area.Random): self.ToggleIsTour(event, 1) elif ch == "V" and OnAnImage: self.WindowsPhotoViewer(event) return elif ch == "W": self.ToggleWrap(event, 1) elif ch == "Y" and not (area.Random | area.IsTour): self.ToggleDynamic(event, 1) elif ch == "Z" and CanZapImage: self.Zap(event) elif ch == "A" and not (area.Dynamic | area.IsTour): self.ToggleRandom(event, 1) # Alt-R doesn't always work for some reason, so we give Alt-A as an alternative else: event.Skip()
I'm well aware that if you apply PEP 8 guidelines strictly it would look like the above. But is this supposed to be an improvement?
I did it to compare and decide -- I'm ambivalent.
My original was a clear, tabular layout making the code structure completely clear, the vertical alignment *highlighting where different cases are treated differently and where they are treated similarly*. This is just an amorphous mass of code that I find much harder to grok. (YMMV but I find it hard to imagine.)
I find it pretty clear -- the pattern and indentation make it just as clear to me that it's a repeated pattern, and the indented form makes it easier for me to match the action to the ch selected. PS Can anyone shed any light on why Alt-R doesn't always "work" on Windows
10? I believe there's some bit of installed software that has hijacked it, but I'm not sure what.
I'm hopeless there .... -Chris B -- Christopher Barker, PhD (Chris) Python Language Consulting - Teaching - Scientific Software Development - Desktop GUI and Web Development - wxPython, numpy, scipy, Cython
On 15/02/2021 01:58, Christopher Barker wrote:
Getting OT here -- you've been warned.
On Sun, Feb 14, 2021 at 2:10 AM Rob Cliffe <rob.cliffe@btinternet.com <mailto:rob.cliffe@btinternet.com>> wrote:
You've broken a number of "rules" code code formatting there ;-)
Thanks for the quotation marks. Indeed, PEP 8 provides guidelines, not diktats.
A big one is aligning things vertically, which, as a rule, I like. but this example does really push things out away from each other, so I'm not so sure.
It's written to suit my editor. I'm with Brendan: restricting the whole world to 80 chars per line is like insisting the whole world program in COBOL.
I agree about the 80 char limit -- personally using usually 90 or 95.
But I do think you need to consider not just your editor -- if anyone else is going to read your code. They're not (in any universe I can imagine).
But in your example, it not only has long lines, but by lining up the stuff after the colon -- there is a LOT of info way over on the right, which I'm not so sure about.
Just for fun, here it is after being run through Black (which I don't usually use ....) -- but I think it looks better that way.
Also -- my first thought looking at that was that it could really benefit from the pattern matching :-)
Yes IMHO it would be a slight improvement. But AFAIU pattern matching is not available yet.
no, it's not. That was a note for me in a way -- I'm a bit of a skeptic about pattern matching, so this is a good example use case.
And my second thought was that there could be a whole other pattern used here -- I usually find long chained elifs can be better expressed as a dict selection, or subclassing, or ...
I cannot imagine how a dictionary, much less a subclass (of what)? could have any relevance here. I'd be interested if you could elucidate.
Again, I don't know what the rest of your code looks like, so maybe none of these would work well, but:
for using a dict, you could set it up something like:
ch_proc_dict = {"B": self.ToggleBackgroundColour, "C": self.ChangeCommonChars, "D": self.DeleteArea, ... } then:
if process_dict[ch](event): return
you also have other checks in there, so those would have to be moved into the functions in the dict, maybe with wrappers (or not -- depends on where you store some of that state data. Exactly. Some wasted effort to turn a simple, contiguous, transparent chunk of code into something more complicated, spread out and harder to understand.
as for subclassing, that's a very different pattern that probably doesn't apply here now that I think about it:
This looks to me like wxPython code (or the like). It is. It's part of a wx.EVT_CHAR handler. for wx.lib.floatcanvas, I don't have keyboard accelerators, but I do have "GUI Modes" classes: Depending on what Mode is set at the moment, each event gets mapped to different actions, so you might have different handling of the character hit depending on what object is selected:
object_selected.process_keystroke(ch)
Of course, you'd need this kind of "switch" construct in the object class anyway. Unless you wanted to get really wordy, and have:
def process_A(self..) ... def process_B(self...) ...
you could be a bit saved in Python:
getattr(selected_object, "process" + ch)(event) Again, turning simple straightforward into more complicated code. (I'm quite capable of writing tricks like that *when I think they're appropriate*; I've done it many times.)
Anyway, as I say -- pretty darn off topic, but those are two of the three ways that I think "switch -- case" can be spelled in Python.
-Chris B
if ch == "B":
self.ToggleBackgroundColour(event) elif ch == "C": self.ChangeCommonChars(event) return elif ch == "D" and len(Areas) > 1: self.DeleteArea(event) return elif ch == "F" and __debug__: self.Info(event) return elif ch == "I": self.ChangeImagesToScroll(event) return elif ch == "J" and self.NUMBER_OF_IMAGES == 1: self.ChangeJustification(event) return elif ch == "L": self.ToggleShowLabels(event) elif ch == "M" and MaxNumberOfImages > 1: self.ChangeNumberOfImages(event) elif ch == "N" and CanZapImage: self.Rename(event) elif ch == "R" and not (area.Dynamic | area.IsTour): self.ToggleRandom(event, 1) elif ch == "S": self.ToggleSets(event, 1) elif ch == "T": self.ChangeTimer(event) return elif ch == "U" and not (area.Dynamic | area.Random): self.ToggleIsTour(event, 1) elif ch == "V" and OnAnImage: self.WindowsPhotoViewer(event) return elif ch == "W": self.ToggleWrap(event, 1) elif ch == "Y" and not (area.Random | area.IsTour): self.ToggleDynamic(event, 1) elif ch == "Z" and CanZapImage: self.Zap(event) elif ch == "A" and not (area.Dynamic | area.IsTour): self.ToggleRandom(event, 1) # Alt-R doesn't always work for some reason, so we give Alt-A as an alternative else: event.Skip()
I'm well aware that if you apply PEP 8 guidelines strictly it would look like the above. But is this supposed to be an improvement?
I did it to compare and decide -- I'm ambivalent. We'll have to agree to differ; I think it's no contest. And as I'm the author and the reader of the code, I have absolute power. 😁
My original was a clear, tabular layout making the code structure completely clear, the vertical alignment *highlighting where different cases are treated differently and where they are treated similarly*. This is just an amorphous mass of code that I find much harder to grok. (YMMV but I find it hard to imagine.)
I find it pretty clear -- the pattern and indentation make it just as clear to me that it's a repeated pattern, and the indented form makes it easier for me to match the action to the ch selected.
PS Can anyone shed any light on why Alt-R doesn't always "work" on Windows 10? I believe there's some bit of installed software that has hijacked it, but I'm not sure what.
I'm hopeless there ....
-Chris B
-- Christopher Barker, PhD (Chris)
Python Language Consulting - Teaching - Scientific Software Development - Desktop GUI and Web Development - wxPython, numpy, scipy, Cython
Still OT ...
But I do think you need to consider not just your editor -- if anyone else is going to read your code.
They're not (in any universe I can imagine).
Exactly -- the most important thing about style is that it be consistent within a project's development team -- if that's just you, then you're all set.
you also have other checks in there, so those would have to be moved into the functions in the dict, maybe with wrappers (or not -- depends on where you store some of that state data.
Exactly. Some wasted effort to turn a simple, contiguous, transparent chunk of code into something more complicated, spread out and harder to understand.
As i said -- depends on the rest of your code. It could make it less spread out and easier to understand :-) For the most part using a dict to switch makes the most sense if the same pattern will be used with multiple "switch dicts".
Again, turning simple straightforward into more complicated code. (I'm quite capable of writing tricks like that *when I think they're appropriate*; I've done it many times.)
I'm not sure I'd call it "tricks" -- but anyway, I've found that big nested ifelses are rarely the cleanest solution -- but not never. - Chris B. -- Christopher Barker, PhD (Chris) Python Language Consulting - Teaching - Scientific Software Development - Desktop GUI and Web Development - wxPython, numpy, scipy, Cython
Still OT ...
But I do think you need to consider not just your editor -- if anyone else is going to read your code.
They're not (in any universe I can imagine).
Exactly -- the most important thing about style is that it be consistent within a project's development team -- if that's just you, then you're all set.
you also have other checks in there, so those would have to be moved into the functions in the dict, maybe with wrappers (or not -- depends on where you store some of that state data.
Exactly. Some wasted effort to turn a simple, contiguous, transparent chunk of code into something more complicated, spread out and harder to understand.
As i said -- depends on the rest of your code. It could make it less spread out and easier to understand :-) I don't understand how it could be less spread out when it starts as a single contiguous piece of code. Perhaps I wasn't clear but when I said "spread out" I meant multiple bits of code in different places in the
On 16/02/2021 05:45, Christopher Barker wrote: program. IOW how many parts it consists of (minimum = 1 = not spread out at all).
For the most part using a dict to switch makes the most sense if the same pattern will be used with multiple "switch dicts".
Again, turning simple straightforward into more complicated code. (I'm quite capable of writing tricks like that *when I think they're appropriate*; I've done it many times.)
I'm not sure I'd call it "tricks" -- but anyway, I've found that big nested ifelses are rarely the cleanest solution -- but not never.
- Chris B.
-- Christopher Barker, PhD (Chris)
Python Language Consulting - Teaching - Scientific Software Development - Desktop GUI and Web Development - wxPython, numpy, scipy, Cython
On Fri, Dec 11, 2020 at 07:41:27PM -0500, Jonathan Crall wrote:
If `__if__` was defined then
``` with Ctx(): print('hi') ```
would only print `hi` if `__if__` returned True. This doesn't require a syntax change.
How do you pass arguments to the "if" dunder? A generalised conditional context manager would look like this: if condition: with Ctx(*args) as obj: block With your suggested syntax, the condition has to be hard-coded into the `__if__` dunder. That's an extremely inflexible API. It also makes it hard to reason about your code, by burying a conditional if deep into the bowels of the context manager. Quick, if this code safe or not? with nuke(target='Woolloomooloo'): fire_missiles() Pity the poor people of Woolloomooloo (which is a real place, in Australia). They have no way of knowing whether or not the missiles are on the way. An explicit test is more flexible, easier to reason about, and much more readable. You suggested: >>> with ub.Cacher('name', cfgstr=ub.hash_data('dependencies')) as cacher: ... data = 'mydata' That looks all the world like data is unconditionally being over-written. The test `data is None` is invisible, and has to be built-into the ub.Cacher class, including the name "data". -- Steve
participants (13)
-
Brendan Barnwell
-
Chris Angelico
-
Christopher Barker
-
David Mertz
-
Greg Ewing
-
Jonathan Crall
-
lasizoillo
-
MRAB
-
Oscar Benjamin
-
Paul Sokolovsky
-
Rob Cliffe
-
Stephen J. Turnbull
-
Steven D'Aprano