• OT: Addition of a .= operator

    From dn@21:1/5 to Rob Cliffe via Python-list on Wed May 24 12:10:09 2023
    On 24/05/2023 10.21, Rob Cliffe via Python-list wrote:

    This sort of code might be better as a single expression. For example:

    user = (
         request.GET["user"]
         .decode("utf-8")
         .strip()
         .lower()
    )
    user = orm.user.get(name=user)


    LOL.  And I thought I was the one with a (self-confessed) tendency to
    write too slick, dense, smart-alec code. 😁
    Indeed, I was itching to shorten it (starting with the low-hanging
    fruit: user = user.strip().lower() ).
    Seriously though: this kind of condensation can come unstuck when any of
    the steps need to be made more complicated.
    ...

    Peter's actual code feels more Pythonic to me.  (It's even 2 lines
    shorter! 😎)

    On 24/05/2023 09.03, Peter J. Holzer wrote:
    I sometimes have a chain of transformations (e.g. first decode it, then strip extra spaces, then normalize spelling, then look it up in a
    database and replace it with the record, ...). Technically, of course
    all these intermediate objects are different, and I could make that
    explicit by using different variable names:

    user_param = request.GET["user"]
    user_decoded = str(user_param, encoding="utf-8")
    user_stripped = user_decoded.strip()
    user_normalized = user_stripped.lower()
    user_object = orm.user.get(name=user_normalized)

    But I find it easier to read if I just reuse the same variable name:

    user = request.GET["user"]
    user = str(user, encoding="utf-8")
    user = user.strip()
    user = user.lower()
    user = orm.user.get(name=user)

    Each instance only has a livetime of a single line (or maybe two or
    three lines if I have to combine variables), so there's little risk of confusion, and reusing the variable name makes it very clear that all
    those intermediate results are gone and won't be used again.


    Once again/recently/another trainee came with a question about input()
    because he expected an number (cf input() providing a string). In this
    case, and to drive-home the point, preferred training illustration is:

    quantity_input = input( "How many would you like? " )
    quantity = float( quantity_input )

    Many others, and I dare so all those who suffer from the aforementioned
    "itch", want to condense this into a single line.

    However, (continuing @Peter's theme) such confuses things when something
    goes wrong - was the error in the input() or in the float()?
    - particularly for 'beginners'
    - and yes, we can expand the above discussion to talk about
    error-handling, and repetition until satisfactory data is input by the
    user or (?frustration leads to) EOD...

    Accordingly, I'd start by favoring @Peter's "explicit" approach. However
    (as mentioned), we end-up with a bunch of names (storage-space) which
    are essentially unused - and many lint-er types will deplore such
    single-use [like it's s-u plastic].

    Thus, advice becomes somewhat the opposite(!) of function parameter
    handling - there, I'm happy with one or two positional-parameters, but
    by three-or-more, this over-taxed brain starts begging/itching for named-parameters instead.

    Now, the "easier" approach appeals. Indeed, if something goes wrong (per input()/float(), above), at least one has the assistance of a
    line-number (where error was noted). Further, using the PyCharm
    Debugger/pdb makes locating the design-error/coder's mistaken
    assumption, a snap.

    A debatable-point is how much we should consider more junior-level
    programmers. IMHO more common functions such as strip() and lower() can
    be combined without much loss of readability or comprehension (apart
    from 'function-chaining' adding complication). Whereas, the other three
    lines are more likely to challenge - and/or become a source of concern
    if mods are requested...

    However, some disagreement - which comes back to why the "_input" suffix (above) acts as an aide-memoire: When "user" is first used (hah!), it is
    the result of an http request. It is then transmogrified, until at the
    end where it is a query-result/object. Yes, however contrived and
    deliberately drawn-out the example, there is still a difference between
    the value which the code first produces (request/input) and that which eventually settles-out (orm/float).

    Accordingly (perhaps), "user_request" or "user_requested" and "user" or "user_record" (according to the way 'that' ORM operates). Perhaps more psychology rather than coding?

    --
    Regards,
    =dn

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Chris Angelico@21:1/5 to dn via Python-list on Wed May 24 10:27:52 2023
    On Wed, 24 May 2023 at 10:12, dn via Python-list <python-list@python.org> wrote:
    However, (continuing @Peter's theme) such confuses things when something
    goes wrong - was the error in the input() or in the float()?
    - particularly for 'beginners'
    - and yes, we can expand the above discussion to talk about
    error-handling, and repetition until satisfactory data is input by the
    user or (?frustration leads to) EOD...

    A fair consideration! Fortunately, Python has you covered.

    $ cat asin.py
    import math

    print(
    math.asin(
    float(
    input("Enter a small number: ")
    )
    )
    )
    $ python3 asin.py
    Enter a small number: 1
    1.5707963267948966
    $ python3 asin.py
    Enter a small number: 4
    Traceback (most recent call last):
    File "/home/rosuav/tmp/asin.py", line 4, in <module>
    math.asin(
    ValueError: math domain error
    $ python3 asin.py
    Enter a small number: spam
    Traceback (most recent call last):
    File "/home/rosuav/tmp/asin.py", line 5, in <module>
    float(
    ValueError: could not convert string to float: 'spam'

    Note that the line numbers correctly show the true cause of the
    problem, despite both of them being ValueErrors. So if you have to
    debug this sort of thing, make sure the key parts are on separate
    lines (even if they're all one expression, as in this example), and
    then the tracebacks should tell you what you need to know.

    ChrisA

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From dn@21:1/5 to Chris Angelico on Wed May 24 17:18:52 2023
    On 24/05/2023 12.27, Chris Angelico wrote:
    On Wed, 24 May 2023 at 10:12, dn via Python-list <python-list@python.org> wrote:
    However, (continuing @Peter's theme) such confuses things when something
    goes wrong - was the error in the input() or in the float()?
    - particularly for 'beginners'
    - and yes, we can expand the above discussion to talk about
    error-handling, and repetition until satisfactory data is input by the
    user or (?frustration leads to) EOD...

    A fair consideration! Fortunately, Python has you covered.

    $ cat asin.py
    import math

    print(
    math.asin(
    float(
    input("Enter a small number: ")
    )
    )
    )
    $ python3 asin.py
    Enter a small number: 1
    1.5707963267948966
    $ python3 asin.py
    Enter a small number: 4
    Traceback (most recent call last):
    File "/home/rosuav/tmp/asin.py", line 4, in <module>
    math.asin(
    ValueError: math domain error
    $ python3 asin.py
    Enter a small number: spam
    Traceback (most recent call last):
    File "/home/rosuav/tmp/asin.py", line 5, in <module>
    float(
    ValueError: could not convert string to float: 'spam'

    Note that the line numbers correctly show the true cause of the
    problem, despite both of them being ValueErrors. So if you have to
    debug this sort of thing, make sure the key parts are on separate
    lines (even if they're all one expression, as in this example), and
    then the tracebacks should tell you what you need to know.


    Yes, an excellent example to show newcomers to make use of 'the
    information *provided*' - take a deep breath and read through it all, picking-out the important information...


    However, returning to "condense this into a single line", the
    frequently-seen coding is (in my experience, at least):

    quantity = float( input( "How many would you like? " ) )

    which would not produce the helpful distinction between line-numbers/function-calls which the above (better-formatted) code does!


    Summarising (albeit IMHO):

    - if relatively trivial/likely to be well-known: collect the calls into
    a chain, eg

    user = user.strip().lower()

    - otherwise use separate lines in order to benefit from the stack-trace

    - (still saying) use separate assignments, rather than chaining more complex/lesser-known combinations

    - ensure that the 'final' identifier is meaningful (and perhaps the
    first, and/or even an 'intermediate' if pertinent or kept for re-use later)

    - perhaps re-use a single identifier-name as a temp-variable, if can
    reasonably 'get away with it'
    (and not confuse simple minds, like yours-truly)


    --
    Regards,
    =dn

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From avi.e.gross@gmail.com@21:1/5 to Chris Angelico on Wed May 24 09:53:47 2023
    It may be a matter of taste and policies, Dave.

    I am talking about whether to write your code so it looks good to you, and dealing with issues like error messages only when needed, or whether to
    first do all kinds of things to catch errors or make it easier if they pop
    up.

    Python can be written fairly compactly and elegantly when trying out an algorithm. But if you pepper it with print statements all over the place showing the current values of variables and return codes (perhaps commented
    out or hiding in an IF statement set to False) then you have a version
    harder to read even if it can potentially be very useful. If your code is constantly specifying what types variables must be or testing constraints,
    it may well compile but for some users all that gets in the way of seeing
    the big picture.

    In this case, we are discussing issues like how to spread code onto multiple lines and opinions differ. In languages that do not use indentation as
    having special meaning, I often like to stretch out and use lots of lines
    for something like a function call with umpteen arguments and especially one containing nested similar dense function calls. A good text editor can be helpful in lining up the code so things at the same indentation level have meaning as do things at other levels.

    I will say that the try/catch type idioms that surround every piece of code, often nested, can make code unreadable.

    Similarly, some languages make it easy to do chaining in ways that use
    multiple lines.

    Since python is (justifiably) picky about indentation, I use such features
    less and more cautiously and sometimes need to carefully do things like add parentheses around a region to avoid inadvertent misunderstandings.

    When I do my work for myself and am not expecting serious errors I tend to write the main program first and only then enhance it as needed. If working with a group and established standards, of course, we follow whatever
    methods are needed, and especially if a large part of the effort is to test thoroughly against requirements.



    -----Original Message-----
    From: Python-list <python-list-bounces+avi.e.gross=gmail.com@python.org> On Behalf Of dn via Python-list
    Sent: Wednesday, May 24, 2023 1:19 AM
    To: python-list@python.org
    Subject: Re: OT: Addition of a .= operator

    On 24/05/2023 12.27, Chris Angelico wrote:
    On Wed, 24 May 2023 at 10:12, dn via Python-list <python-list@python.org>
    wrote:
    However, (continuing @Peter's theme) such confuses things when something
    goes wrong - was the error in the input() or in the float()?
    - particularly for 'beginners'
    - and yes, we can expand the above discussion to talk about
    error-handling, and repetition until satisfactory data is input by the
    user or (?frustration leads to) EOD...

    A fair consideration! Fortunately, Python has you covered.

    $ cat asin.py
    import math

    print(
    math.asin(
    float(
    input("Enter a small number: ")
    )
    )
    )
    $ python3 asin.py
    Enter a small number: 1
    1.5707963267948966
    $ python3 asin.py
    Enter a small number: 4
    Traceback (most recent call last):
    File "/home/rosuav/tmp/asin.py", line 4, in <module>
    math.asin(
    ValueError: math domain error
    $ python3 asin.py
    Enter a small number: spam
    Traceback (most recent call last):
    File "/home/rosuav/tmp/asin.py", line 5, in <module>
    float(
    ValueError: could not convert string to float: 'spam'

    Note that the line numbers correctly show the true cause of the
    problem, despite both of them being ValueErrors. So if you have to
    debug this sort of thing, make sure the key parts are on separate
    lines (even if they're all one expression, as in this example), and
    then the tracebacks should tell you what you need to know.


    Yes, an excellent example to show newcomers to make use of 'the
    information *provided*' - take a deep breath and read through it all, picking-out the important information...


    However, returning to "condense this into a single line", the
    frequently-seen coding is (in my experience, at least):

    quantity = float( input( "How many would you like? " ) )

    which would not produce the helpful distinction between line-numbers/function-calls which the above (better-formatted) code does!


    Summarising (albeit IMHO):

    - if relatively trivial/likely to be well-known: collect the calls into
    a chain, eg

    user = user.strip().lower()

    - otherwise use separate lines in order to benefit from the stack-trace

    - (still saying) use separate assignments, rather than chaining more complex/lesser-known combinations

    - ensure that the 'final' identifier is meaningful (and perhaps the
    first, and/or even an 'intermediate' if pertinent or kept for re-use later)

    - perhaps re-use a single identifier-name as a temp-variable, if can
    reasonably 'get away with it'
    (and not confuse simple minds, like yours-truly)


    --
    Regards,
    =dn
    --
    https://mail.python.org/mailman/listinfo/python-list

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Peter J. Holzer@21:1/5 to dn via Python-list on Wed May 24 21:09:05 2023
    On 2023-05-24 12:10:09 +1200, dn via Python-list wrote:
    Perhaps more psychology rather than coding?

    Both. As they say, coding means writing for other people first, for
    the computer second. So that means anticipating what will be least
    confusing for that other person[1] who's going to read that code.

    hp

    [1] Which is often yourself, a few months older. Or it could be an
    experienced colleague who's very familiar with the codebase. Or a new
    colleague trying to understand what this is all about (possibly while
    learning Python).

    --
    _ | Peter J. Holzer | Story must make more sense than reality.
    |_|_) | |
    | | | hjp@hjp.at | -- Charles Stross, "Creative writing
    __/ | http://www.hjp.at/ | challenge!"

    -----BEGIN PGP SIGNATURE-----

    iQIzBAABCgAdFiEETtJbRjyPwVTYGJ5k8g5IURL+KF0FAmRuYMwACgkQ8g5IURL+ KF0cwA/+IKhyhVGe5Z/3+lfLVrmwpY6kXOKoWjqRnmzYqLkgTpBpCzQb4UZBhcwC AUDoo8rOXI0KEjpf0xoHafwPRsM0v2coeWeCsnwoAHerLgX9VHeK75h+cFLpVgXe KTN1j9URP0aYK/PaV6Y4zdMec/5rmFJXmyLdBt8+yx7ngdD1mTx8hNMAbCiTsnuN zxLpEalFT6lB5HsLb7bZmh8qFfZacmgwOp8pHOzxoohM4/c3EtSFxFAXDnNE33fC sAj0CX5ebiTSLjgrdwqaPyfipXfJ1NaH3mVigOGZbDgb4bE7wELyvyiWS7F/xhWY xkQ4jmKHK4Fd7RvNQVoztkslP8DiQqYonKOtnpwGadMIrcScW/Uz83Hxe2LdIuPx 78MN2C+/COR4taSlb7osn9OWlo4In1iO2L12Nr2QgnIeQdHYgkiKfrfhQrfcHbpc fkRezp5A9Q3B/HK8TI5+P7L1Mgs3NJ9Vc+wfbaNY/ua6Uq9t5vasPbf01ErheRZx aKnrocUp2vsJMV943XXoKOaXN4GDwVNyH9dM009+wQSI3TFIO+UU2UeZaNtDnyQt QKK5OFBG6aLmcHsW7atHBFTR/XS0J+LYe4hbOZhv3IcI9nRCroIu0pUg+aX7TTLJ +Q/rzuuVPLcUghrC0TsWeVKyOipi8i2L9NxpXFP
  • From Simon Ward@21:1/5 to dn via Python-list on Fri Jun 9 16:41:42 2023
    On Wed, May 24, 2023 at 05:18:52PM +1200, dn via Python-list wrote:
    Note that the line numbers correctly show the true cause of the
    problem, despite both of them being ValueErrors. So if you have to
    debug this sort of thing, make sure the key parts are on separate
    lines (even if they're all one expression, as in this example), and
    then the tracebacks should tell you what you need to know.


    Yes, an excellent example to show newcomers to make use of 'the
    information *provided*' - take a deep breath and read through it all, >picking-out the important information...


    However, returning to "condense this into a single line", the
    frequently-seen coding is (in my experience, at least):

    quantity = float( input( "How many would you like? " ) )

    which would not produce the helpful distinction between >line-numbers/function-calls which the above (better-formatted) code
    does!

    Old thread I know, but thought it was worth pointing out that Python 3.11 brought in fine-graned error locations in tracebacks[1]:

    $ python3.10 asin.py
    Enter a small number: 4
    Traceback (most recent call last):
    File "/home/p10365088/asin.py", line 3, in <module>
    print(math.asin(float(input("Enter a small number: "))))
    ValueError: math domain error

    $ python3.11 asin.py
    Enter a small number: 4
    Traceback (most recent call last):
    File "/home/p10365088/asin.py", line 3, in <module>
    print(math.asin(float(input("Enter a small number: "))))
    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    ValueError: math domain error

    [1]: https://docs.python.org/3/whatsnew/3.11.html#whatsnew311-pep657

    Regards,
    Simon
    --
    A complex system that works is invariably found to have evolved from a
    simple system that works.—John Gall

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)