thestp123 thestp123 - 1 month ago 18
Python Question

Shorten loop to list comprehension with multiple for's and ifs/elifs

I have this code, where the dictionary keys are functions and the values are lists which contains keywords. It searches through a listed

t
and it sees if it's in a list from the values of the dictionary. If it is, then it runs the function [key] of that value. If not, it runs the
NowThis
function. The
count
variable is there just so that in the case
t
being
'hello hi'
.

dct = {doThis:['hi','hello'],
doThat:['bye','goodbye']}

t = 'hi there' # or 'test'|'goodbye'|'hello hi'
count=0
for listValue in t.split():
if count > 1 or count < 0:
break
elif listValue in [n for v in dct.values() for n in v]:
for key,vl in dct.iteritems():
if listValue in vl:
key()
count+=1
elif count==0:
nowThis()
count -=1


I did have a previous version of this code where it was just for if for if, and I was easily able to turn that into a list comprehension:

[key() for listValue in t.split() if listValue in [n for v in dct.values() for n in v] for key,vl in dct.iteritems() if listValue in vl]


However, I am having trouble turning the current code into something like that list comprehension.

Answer

As has been explained in the comments there is no benefit in using a list comprehension here. It would actually be slower, since you'd be creating a list you don't actually want. Also, it is considered poor design to use a list comp purely for its side effects.

As I mentioned in the comments, your dictionary design is the wrong way around, so you aren't getting the benefits of using a dictionary, i.e., a dict can quickly test if a key is present, and it can quickly retrieve the value associated with a key.

Assuming your current code does what you want, here's a better way to write it.

def do_this():
    print 'Do This!\n'

def do_that():
    print 'Do That!\n'

def now_this():
    print 'Now This!\n'

dct = {
    'hi': do_this,
    'hello':  do_this,
    'bye': do_that,
    'goodbye': do_that,
}

data = ('hi there', 'python', 'goodbye hello', '')
for t in data:
    v = t.split(None, 1)[0] if t else ''
    print [t, v]
    dct.get(v, now_this)()

output

['hi there', 'hi']
Do This!

['python', 'python']
Now This!

['goodbye hello', 'goodbye']
Do That!

['', '']
Now This!

Here's a more compact version of that for loop without the print statements:

for t in data:
    dct.get(t.split(None, 1)[0] if t else '', now_this)()

We use a conditional expression (t.split(None, 1)[0] if t else '') so we can handle when t is an empty string. Here's an alternative version that's less readable. :)

for t in data:
    dct.get((t.split(None, 1) or [''])[0], now_this)()

If you can guarantee that t will never be an empty string, then you can use the simple version:

for t in data:
    dct.get(t.split(None, 1)[0], now_this)()