• Local variable definition in Python list comprehension

    From James Tsai@21:1/5 to All on Thu Sep 1 03:31:25 2022
    Hello,

    I find it very useful if I am allowed to define new local variables in a list comprehension. For example, I wish to have something like
    [(x, y) for x in range(10) for y := x ** 2 if x + y < 80], or
    [(x, y) for x in range(10) with y := x ** 2 if x + y < 80].

    For now this functionality can be achieved by writing
    [(x, y) for x in range(10) for y in [x ** 2] if x + y < 80].

    Is it worthwhile to add a new feature like this in Python? If so, how can I propose this to PEP?

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Ben Bacarisse@21:1/5 to James Tsai on Thu Sep 1 15:14:55 2022
    James Tsai <jamestztsai@gmail.com> writes:

    I find it very useful if I am allowed to define new local variables in
    a list comprehension. For example, I wish to have something like
    [(x, y) for x in range(10) for y := x ** 2 if x + y < 80], or
    [(x, y) for x in range(10) with y := x ** 2 if x + y < 80].

    For now this functionality can be achieved by writing
    [(x, y) for x in range(10) for y in [x ** 2] if x + y < 80].

    x and y are, to a first approximation, new local variables defined in a
    list comprehension. I think you need to restate what it is you want.

    Is it worthwhile to add a new feature like this in Python? If so, how
    can I propose this to PEP?

    To make any sort of case you'd need to give an example that does not
    have a clearer way to write it already. Your working version is, to me, clearer that the ones you want to be able to write.

    --
    Ben.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Chris Angelico@21:1/5 to James Tsai on Fri Sep 2 02:15:28 2022
    On Fri, 2 Sept 2022 at 02:10, James Tsai <jamestztsai@gmail.com> wrote:

    Hello,

    I find it very useful if I am allowed to define new local variables in a list comprehension. For example, I wish to have something like
    [(x, y) for x in range(10) for y := x ** 2 if x + y < 80], or
    [(x, y) for x in range(10) with y := x ** 2 if x + y < 80].

    For now this functionality can be achieved by writing
    [(x, y) for x in range(10) for y in [x ** 2] if x + y < 80].

    Is it worthwhile to add a new feature like this in Python? If so, how can I propose this to PEP?

    Not everything has to be a one-liner.

    ChrisA

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Eryk Sun@21:1/5 to James Tsai on Thu Sep 1 11:34:07 2022
    On 9/1/22, James Tsai <jamestztsai@gmail.com> wrote:

    I find it very useful if I am allowed to define new local variables in a
    list comprehension. For example, I wish to have something like
    [(x, y) for x in range(10) for y := x ** 2 if x + y < 80], or
    [(x, y) for x in range(10) with y := x ** 2 if x + y < 80].

    For now this functionality can be achieved by writing
    [(x, y) for x in range(10) for y in [x ** 2] if x + y < 80].

    You can assign a local variable in the `if` expression. For example:

    >>> [(x, y) for x in range(10) if x + (y := x**2) < 30]
    [(0, 0), (1, 1), (2, 4), (3, 9), (4, 16)]

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Chris Angelico@21:1/5 to James Tsai on Fri Sep 2 07:01:31 2022
    On Fri, 2 Sept 2022 at 06:55, James Tsai <jamestztsai@gmail.com> wrote:

    在 2022年9月1日星期四 UTC+2 18:34:36,<ery...@gmail.com> 写道:
    On 9/1/22, James Tsai <james...@gmail.com> wrote:

    I find it very useful if I am allowed to define new local variables in a list comprehension. For example, I wish to have something like
    [(x, y) for x in range(10) for y := x ** 2 if x + y < 80], or
    [(x, y) for x in range(10) with y := x ** 2 if x + y < 80].

    For now this functionality can be achieved by writing
    [(x, y) for x in range(10) for y in [x ** 2] if x + y < 80].
    You can assign a local variable in the `if` expression. For example:

    [(x, y) for x in range(10) if x + (y := x**2) < 30]
    [(0, 0), (1, 1), (2, 4), (3, 9), (4, 16)]

    Yeah this works great but like [(x, y) for x in range(10) for y in [x**2]] I written before, is kind of a hack. And if initially I do not need an "if" condition in the list comprehension, this becomes less convenient. I still can write
    [(x, y) for x in range(10) if (y := x**2) or True]

    But I wonder if Python could have a specific syntax to support this.


    But why would you need to assign to y in that example? If you're using
    it more than once, you can use :=, and if you aren't, you don't need
    to. But do be aware that := does not create a comprehension-local name
    binding, but a nonlocal instead.

    No but very often when I have written a neat list/dict/set comprehension, I find it very necessary
    to define local variable(s) to make it more clear and concise. Otherwise I have to break it down
    to several incrementally indented lines of for loops, if statements, and variable assignments,
    which I think look less nice.

    Well, if it's outgrown a list comp, write it on multiple lines. Like I
    said, not everything has to be a one-liner.

    ChrisA

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From James Tsai@21:1/5 to this becomes less convenient. I sti on Thu Sep 1 13:33:16 2022
    在 2022年9月1日星期四 UTC+2 18:34:36,<ery...@gmail.com> 写道:
    On 9/1/22, James Tsai <james...@gmail.com> wrote:

    I find it very useful if I am allowed to define new local variables in a list comprehension. For example, I wish to have something like
    [(x, y) for x in range(10) for y := x ** 2 if x + y < 80], or
    [(x, y) for x in range(10) with y := x ** 2 if x + y < 80].

    For now this functionality can be achieved by writing
    [(x, y) for x in range(10) for y in [x ** 2] if x + y < 80].
    You can assign a local variable in the `if` expression. For example:

    [(x, y) for x in range(10) if x + (y := x**2) < 30]
    [(0, 0), (1, 1), (2, 4), (3, 9), (4, 16)]

    Yeah this works great but like [(x, y) for x in range(10) for y in [x**2]] I written before, is kind of a hack. And if initially I do not need an "if" condition in the list comprehension, this becomes less convenient. I still can write
    [(x, y) for x in range(10) if (y := x**2) or True]

    But I wonder if Python could have a specific syntax to support this.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From James Tsai@21:1/5 to All on Thu Sep 1 13:26:28 2022
    在 2022年9月1日星期四 UTC+2 18:16:03,<Chris Angelico> 写道:
    On Fri, 2 Sept 2022 at 02:10, James Tsai <james...@gmail.com> wrote:

    Hello,

    I find it very useful if I am allowed to define new local variables in a list comprehension. For example, I wish to have something like
    [(x, y) for x in range(10) for y := x ** 2 if x + y < 80], or
    [(x, y) for x in range(10) with y := x ** 2 if x + y < 80].

    For now this functionality can be achieved by writing
    [(x, y) for x in range(10) for y in [x ** 2] if x + y < 80].

    Is it worthwhile to add a new feature like this in Python? If so, how can I propose this to PEP?
    Not everything has to be a one-liner.

    ChrisA

    No but very often when I have written a neat list/dict/set comprehension, I find it very necessary to define local variable(s) to make it more clear and concise. Otherwise I have to break it down to several incrementally indented lines of for loops, if
    statements, and variable assignments, which I think look less nice.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From James Tsai@21:1/5 to All on Thu Sep 1 13:18:53 2022
    在 2022年9月1日星期四 UTC+2 16:15:17,<Ben Bacarisse> 写道:
    James Tsai <james...@gmail.com> writes:

    I find it very useful if I am allowed to define new local variables in
    a list comprehension. For example, I wish to have something like
    [(x, y) for x in range(10) for y := x ** 2 if x + y < 80], or
    [(x, y) for x in range(10) with y := x ** 2 if x + y < 80].

    For now this functionality can be achieved by writing
    [(x, y) for x in range(10) for y in [x ** 2] if x + y < 80].
    x and y are, to a first approximation, new local variables defined in a
    list comprehension. I think you need to restate what it is you want.
    Is it worthwhile to add a new feature like this in Python? If so, how
    can I propose this to PEP?
    To make any sort of case you'd need to give an example that does not
    have a clearer way to write it already. Your working version is, to me, clearer that the ones you want to be able to write.

    --
    Ben.

    By local variable definition I mean binding a variable to a single value, so it doesn't include giving an iterable that a variable can take values iteratively, e.g. 'for x in range(10)'. Does it not worth introducing a specific syntax to do this, instead
    of creating a new list ad hoc to define the variable like 'for y in [1]'?

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Peter J. Holzer@21:1/5 to James Tsai on Fri Sep 2 00:11:56 2022
    On 2022-09-01 13:33:16 -0700, James Tsai wrote:
    在 2022年9月1日星期四 UTC+2 18:34:36,<ery...@gmail.com> 写道:
    On 9/1/22, James Tsai <james...@gmail.com> wrote:

    I find it very useful if I am allowed to define new local variables in a list comprehension. For example, I wish to have something like
    [(x, y) for x in range(10) for y := x ** 2 if x + y < 80], or
    [(x, y) for x in range(10) with y := x ** 2 if x + y < 80].

    For now this functionality can be achieved by writing
    [(x, y) for x in range(10) for y in [x ** 2] if x + y < 80].
    You can assign a local variable in the `if` expression. For example:

    [(x, y) for x in range(10) if x + (y := x**2) < 30]
    [(0, 0), (1, 1), (2, 4), (3, 9), (4, 16)]

    Yeah this works great but like [(x, y) for x in range(10) for y in
    [x**2]] I written before, is kind of a hack. And if initially I do not
    need an "if" condition in the list comprehension, this becomes less convenient. I still can write
    [(x, y) for x in range(10) if (y := x**2) or True]

    In that case
    [(x, x**2) for x in range(10)]
    seems to be somewhat more readable.

    I would also say that
    [(x, x**2) for x in range(10) if x + x**2 < 80]
    doesn't really seem worse than any of the variants with y in it.
    (Yes, I get that your real duplicated expression is probably a bit more
    complex than `x**2`, but by the time a temporary variable really
    improves readability it's probably a good time to split that across
    multiple lines, too.)

    hp

    --
    _ | 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+KF0FAmMRLicACgkQ8g5IURL+ KF1VfBAAoBcrQIgDt19W0Oi+qlsd/WsZU5XDiN2xfiYH20B7/U949D6mqWnv36fV k0ftHQr4JHAYDnH+sSM6XWZlqx5cBsZlWJlFa8A1Ghya8KKDZA17VTO3CwpbDe3o jcHAQhDtbSC19lreAFctQRtNDBRjI3BYCnCDa+gxDgo/76Qkmpeb0fKgAkuXk259 nuP/swGGG1I1lVhF9Ce4mVHNGPczDq8mrekmhDH3xk96yoQQX8BtgfTWhOlTXIxk sjjgtiHIYBAIke/XnxCFjas4dFp4CImulQLW+PAXGQ7uytiwyn8k2cvGVNjzGwmC AhVpg9hP5OPgakjYhU0bzWoSKkj4odTMUta5UY1v/JOc+i3LdgV1IcGyJg88vA8j b4Trxj0vc6tJ/WcaIrtaEL13dxOY5T/hBRoIULEvs76vgqJjjy2Cfl2YZgBN/+J5 auuxxTMWIDxjzo6wA2Flu6Cxib/S7XzTLQU6jzcvb58ZCcDPPTKu7w81KEw76OlJ dlUYQaQF1madiMCg5/QCqdaNyyUpLIzYBdZ1uNfj0KOPlxfIbU2uN5uPPkOWYfEA HKpp2U/zynr3DCBLxKoF9zfGYABuodRhRYO5kgFMI58+3FD4Xt2cBxizoIOy3ekd fDNXqkn+gurabb0HiKvjGO77yQ/T2I17pgfZpfT
  • From Cameron Simpson@21:1/5 to Chris Angelico on Fri Sep 2 08:11:06 2022
    On 02Sep2022 07:01, Chris Angelico <rosuav@gmail.com> wrote:
    On Fri, 2 Sept 2022 at 06:55, James Tsai <jamestztsai@gmail.com> wrote:
    No but very often when I have written a neat list/dict/set
    comprehension, I find it very necessary
    to define local variable(s) to make it more clear and concise. Otherwise I have to break it down
    to several incrementally indented lines of for loops, if statements, and variable assignments,
    which I think look less nice.

    Well, if it's outgrown a list comp, write it on multiple lines. Like I
    said, not everything has to be a one-liner.

    True, but a comprehension can be more expressive than a less
    "functional" expression (series of statements).

    James, can you provide (a) a real world example where you needed to
    write a series of statements or loops and (b) a corresponding example of
    how you would have preferred to have written that code, possibly
    inventing some syntax or misusing ":=" as if it workeed they way you'd
    like it to work?

    Cheers,
    Cameron Simpson <cs@cskk.id.au>

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Avi Gross@21:1/5 to rosuav@gmail.com on Thu Sep 1 18:52:49 2022
    Dumb question. Your y is purely a function of x. So create an f(x) where
    you want your y. It probably can even be anonymous inline. I mean your
    return values of (x, y) would be (x, f(x)) ...

    On Thu, Sep 1, 2022, 5:04 PM Chris Angelico <rosuav@gmail.com> wrote:

    On Fri, 2 Sept 2022 at 06:55, James Tsai <jamestztsai@gmail.com> wrote:

    在 2022年9月1日星期四 UTC+2 18:34:36,<ery...@gmail.com> 写道:
    On 9/1/22, James Tsai <james...@gmail.com> wrote:

    I find it very useful if I am allowed to define new local variables
    in a
    list comprehension. For example, I wish to have something like
    [(x, y) for x in range(10) for y := x ** 2 if x + y < 80], or
    [(x, y) for x in range(10) with y := x ** 2 if x + y < 80].

    For now this functionality can be achieved by writing
    [(x, y) for x in range(10) for y in [x ** 2] if x + y < 80].
    You can assign a local variable in the `if` expression. For example:

    [(x, y) for x in range(10) if x + (y := x**2) < 30]
    [(0, 0), (1, 1), (2, 4), (3, 9), (4, 16)]

    Yeah this works great but like [(x, y) for x in range(10) for y in
    [x**2]] I written before, is kind of a hack. And if initially I do not need an "if" condition in the list comprehension, this becomes less convenient.
    I still can write
    [(x, y) for x in range(10) if (y := x**2) or True]

    But I wonder if Python could have a specific syntax to support this.


    But why would you need to assign to y in that example? If you're using
    it more than once, you can use :=, and if you aren't, you don't need
    to. But do be aware that := does not create a comprehension-local name binding, but a nonlocal instead.

    No but very often when I have written a neat list/dict/set
    comprehension, I find it very necessary
    to define local variable(s) to make it more clear and concise. Otherwise
    I have to break it down
    to several incrementally indented lines of for loops, if statements, and
    variable assignments,
    which I think look less nice.

    Well, if it's outgrown a list comp, write it on multiple lines. Like I
    said, not everything has to be a one-liner.

    ChrisA
    --
    https://mail.python.org/mailman/listinfo/python-list


    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From James Tsai@21:1/5 to All on Fri Sep 2 15:28:39 2022
    在 2022年9月2日星期五 UTC+2 00:17:23,<cameron...@gmail.com> 写道:
    On 02Sep2022 07:01, Chris Angelico <ros...@gmail.com> wrote:
    On Fri, 2 Sept 2022 at 06:55, James Tsai <james...@gmail.com> wrote:
    No but very often when I have written a neat list/dict/set
    comprehension, I find it very necessary
    to define local variable(s) to make it more clear and concise. Otherwise I have to break it down
    to several incrementally indented lines of for loops, if statements, and variable assignments,
    which I think look less nice.

    Well, if it's outgrown a list comp, write it on multiple lines. Like I >said, not everything has to be a one-liner.
    True, but a comprehension can be more expressive than a less
    "functional" expression (series of statements).

    James, can you provide (a) a real world example where you needed to
    write a series of statements or loops and (b) a corresponding example of
    how you would have preferred to have written that code, possibly
    inventing some syntax or misusing ":=" as if it workeed they way you'd
    like it to work?

    Cheers,
    Cameron Simpson <c...@cskk.id.au>

    Yeah, I think list comprehension is particularly useful to construct a deeply nested list/dict. For example, I am now using Plotly to visualize a cellular network including several base stations and users. Here is the function I have written:

    def plot_network(area, base_stations, users):
    bs_poses = np.array([bs.pos for bs in base_stations])
    ue_poses = np.array([ue.pos for ue in users])
    fig = px.scatter(x=bs_poses[:, 0], y=bs_poses[:, 1])
    fig.add_scatter(x=ue_poses[:, 0], y=ue_poses[:, 1])
    fig.update_layout(
    xaxis=dict(range=[0, area[0]], nticks=5),
    yaxis=dict(range=[0, area[1]], nticks=5),
    shapes=[dict(
    type="circle",
    fillcolor="PaleTurquoise",
    x0=x-r, y0=y-r, x1=x+r, y1=y+r,
    hovertext=f"({x:.2f}, {y:.2f})",
    opacity=0.3
    ) for bs in base_stations for x, y in [bs.pos]
    for r in [bs.cell_radius]],
    )
    return fig

    Simply put, I want to scatter the BSs and users, and additionally I want to draw a big circle around each BS to represent its cell coverage. I can choose to write 'x0=bs.pos[0]-bs.cell_radius, y0=...' instead, but it becomes less concise, and if x, y, or
    r is the return value of a function instead of a property, it becomes more computationally expensive to repeat calling the function as well. I also can create the list of 'shapes' by appending to a list, like

    shapes = []
    for bs in base_stations:
    x, y = bs.pos
    r = bs.cell_radius
    shapes.append(dict(...))
    fig.update_layout(
    xaxis=dict(range=[0, area[0]], nticks=5),
    yaxis=dict(range=[0, area[1]], nticks=5),
    shapes=shapes
    )

    But in my opinion this is much less concise. I think it looks better to create the list within the nested structure. So I totally agree that list comprehension adds much expressiveness in Python. I only wonder whether it is a good idea to introduce a
    specific syntax for local variable assignment in list comprehensions, instead of using "for r in [bs.cell_radius]".
    I am also surprised to know that the assignment operator ":=" in a list comprehension will assign a variable outside of the scope of the comprehension. I think it does not make sense since a list comprehension without a ":=" will never change name
    bindings outside itself.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Dan Stromberg@21:1/5 to rosuav@gmail.com on Fri Sep 2 16:43:03 2022
    On Thu, Sep 1, 2022 at 9:16 AM Chris Angelico <rosuav@gmail.com> wrote:

    On Fri, 2 Sept 2022 at 02:10, James Tsai <jamestztsai@gmail.com> wrote:

    Hello,

    I find it very useful if I am allowed to define new local variables in a
    list comprehension. For example, I wish to have something like
    [(x, y) for x in range(10) for y := x ** 2 if x + y < 80], or
    [(x, y) for x in range(10) with y := x ** 2 if x + y < 80].

    For now this functionality can be achieved by writing
    [(x, y) for x in range(10) for y in [x ** 2] if x + y < 80].

    Is it worthwhile to add a new feature like this in Python? If so, how
    can I propose this to PEP?

    Not everything has to be a one-liner.

    So true!

    I like list comprehensions and generator expressions, but sometimes I end
    up regretting their use when there's a bug, and I have to convert one to a
    for loop + list.append in order to debug.

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