[Tutor] all right students, what do we learn

Alan Gauld alan.gauld at btinternet.com
Sun Nov 2 23:02:48 CET 2014


On 02/11/14 20:05, Clayton Kirkwood wrote:

>>> out that python doesn't seem to like an assignment in the overall if
>>> clause - error.
>>
>> Correct, the if statement requires an expression (specifically a boolean
>> expression). And assignments are not expressions.
>
> My thinking was that I was making a Boolean value:
>
>
> ]=[(reenter, key) for key in key_list if
>>> ((key in key_list0)) or (print("Error:", key, "not available, start
>>> again") and exec('reenter = True', ))]

The assignment does not make a value it assigns a value to a variable 
but that value is not exposed in the code containing the assignment.
You can see that using the interpreter:

 >>> result = exec('x=42')
 >>> print (result)
None
 >>> print(x)
42
 >>>

You can see that the exec returned None to result.
The value of x was changed but the assignment itself
had no value. It is not a useful expression because
it will always return None which equates to false
in boolean terms.

Lets now look at what your conditional expression is doing:

(key in key_list0) or
  ( print("Error:", key, "not available, start again") and
    exec('reenter = True'))

If key is in the list the evaluation stops there and the result plus the 
current value of reenter are put in the output list.

If the key is not in the list the second part of the or is evaluated.
The print executes and returns the string it is given which is True.
So the second part of the AND is now evaluated which is the exec. The 
exec always returns None or False.

So the if condition looks like:

False OR (True AND False)

which simplifies to: False or False

wjich is False.

So your if condition is only ever true when 'key in key_list0'
is true.

Note too that reenter will be true as soon as you hit a case where the 
key is not in the list and will stay true thereafter, it will never go 
back to false.

I suspect that's not the behaviour you are looking for.

> In the first case, key is in key_list0 so that is a True and we are finished
> for that key. In the other case, key is not in key_list0 so we go on past
> the or. I print which returns a non-False, ergo a True, and then I make an
> assignment which should be a True.

The exec assigns a value to reenter but returns None which is False.

> True. Assignments create a Boolean True (or should).

No they shouldn't. Assignments are not boolean expressions and cannot be 
used as such

if x=42:
   print(x)

results in a syntax error.

> The other issue is that I had the outside reenter set as true to start the
> for and turn it False so that the for won't re-occur unless I have a bad key
> and force reenter to be a True.

But the reenter inside the comprehension is an entirely different 
reenter to the one outside. Your exec has no effect on it.

>>> Which I don't quite understand. The reason is because I set up the if
>>> statement to be True either way. With an   a   it would go thru and
>>> create the proper output.

But as shown above you dodn't, it is only true when the 'in' test is true.

> What I would expect from [('a', 'apple'), ('b', 'banana')] and entering a y
> Would be:
> [('a',True), ('y', False)]
>
> If I entered only 'y' I would expect
> [('y', False)]		but this won't work because I can't use the '=' in
> the if clause even though it returns a True

OK, I got that but what are you trying to achieve in the context of your 
problem? Is it to get a set of valid keys and values? In other words the 
subset of your original data corresponding to the users input list? If 
so I think there are far simpler ways to go about it, see below. I 
suspect you are making the root solution more complex by the choice of 
returning the true/false flag with each key.

>> "Keep asking the user for keys until they hit upon a list that is all
>> valid and create a list of corresponding values."
>>
>> Is that correct?
>
> Absotively
>
>>
>> If so I'd write it like this:
>>
>> prompt = 'Please enter space separated keys in the order you want: '
>> while True:  # keep going round till we break
>>     key_list = input(prompt).split()
>>     if all(key in key_list0 for key in key_list):
>>        break
>>
>> result = [(key, key_columns) for key in key_list]
>>
>> Which would give you a list containing each user key and its
>> corresponding value from key_list0.
>
> Yes, this approaches what my goal is, but it doesn't indicate which if any
> bad keys ([pair[0]) are there, ergo, wanting to return a False if the key
> didn't exist in both lists. Your snippet definitely removes all bad keys,
> however.

Yes, that's the point, it only allows good keys through so you don't 
need to collect the bad keys. If you wanted to tell the user which keys 
were invalid then modify the loop code to report the faulty one.

prompt = 'Please enter space separated keys in the order you want: '
while True:  # keep going round till we break
     key_list = input(prompt).split()
     bad_keys = [key in key_list0 for key not in key_list]
     if not bad_keys:
         break
     else: print("The following keys were invalid: ",bad_keys)

You could even put that in a function:

def get_valid_keys():
     prompt = 'Please enter space separated keys in the order you want: '
     while True:  # keep going round till we break
         key_list = input(prompt).split()
         bad_keys = [key in key_list0 for key not in key_list]
         if not bad_keys:
             return key_list
         else: print("The following keys were invalid: ",bad_keys)

-- 
Alan G
Author of the Learn to Program web site
http://www.alan-g.me.uk/
http://www.flickr.com/photos/alangauldphotos



More information about the Tutor mailing list