Local variable definition in Python list comprehension

James Tsai jamestztsai at gmail.com
Fri Sep 2 18:28:39 EDT 2022


在 2022年9月2日星期五 UTC+2 00:17:23,<cameron... at gmail.com> 写道:
> On 02Sep2022 07:01, Chris Angelico <ros... at gmail.com> wrote: 
> >On Fri, 2 Sept 2022 at 06:55, James Tsai <james... at 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... at 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.


More information about the Python-list mailing list