简体   繁体   中英

list comprehension variable scope with nested ifs in python 3

Based on this answer and the fact that list comprehensions no longer "leak" their variable in Python 3.x how I can implement/rewrite this expression in Python 3?

>>> import sys
>>> sys.version[:5]
'3.6.5'
>>> import psutil
>>> psutil.__version__
'5.4.7'
>>> [port.laddr.port for proc in psutil.process_iter(attrs=['name']) if 'sshd' in proc.info['name'] if any([port.status == psutil.CONN_LISTEN for port in proc.connections()])]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 1, in <listcomp>
NameError: name 'port' is not defined

First, that list comprehension is pretty whack stylistically. Totally incomprehensible for anyone trying to read it. Indentation is your friend, and numbers of lines of code are not a premium. Changing the indentation to make it more readable:

ports = [
    port.laddr.port
    for proc in psutil.process_iter(attrs=['name'])
    if 'sshd' in proc.info['name']
    if any([port.status == psutil.CONN_LISTEN for port in proc.connections()])
]

The problem now becomes clear. List comprehensions do not leak their scope in Python 3. Outside of the ports list comprehension (if it didn't fail due to a NameError ), there would be no object called proc .

That said, you do get a name error because the list comprehension inside your call to any also doesn't leak its scope. No port variable escapes outside to the parent list comprehension, and you get a NameError .

Second, to address your question, you should probably avoid a list comprehension altogether. You're trying to do too much in a single statement. Create a ports list. Iterate through your processes in a for loop, and append according to your required logic.

To demonstrate:

ports = []
def validate_proc(proc):
    return any(
        port.status == psutil.CONN_LISTEN
        for port in proc.connections()
    )
for proc in in psutil.process_iter(attrs=['name']):
    if not 'sshd' in proc.info['name']:
        continue
    if not validate_proc(proc):
        continue
    for port in proc.connections():
        ports.append(port.laddr.port)

Here, I've assumed that you wanted all ports for a given process if any of the ports match your given criterion, and you otherwise want none of them. This is how I read your comprehension. If that's not what you're looking for, then I can change it. (This is a concrete example of why using large list comprehensions should be avoided.)

Rather than using any and expecting the list comprehension variable to leak out, you can find the relevant ports using properly qualified comprehensions.

I've separated it into two comprehensions so it would fit in my brain.

procs = [proc for proc in psutil.process_iter(attrs=['name'])
         if 'sshd' in proc.info['name']]
ports = [port.latter.port for proc in procs
         for port in proc.connections() if port.status==psutil.CONN_LISTEN]

print(ports)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM