I'm not talking about the ternary operator. Can I write an if else statement in one line outside of a value expression? I want to shorten this code.
if x == 'A':
return True
if x == 'B':
return True
if x == 'C':
return True
return False
You can use the in operator like this:
return x in ('A', 'B', 'C')
For Python 3.2+:
return x in {'A', 'B', 'C'}
From docs:
Python’s peephole optimizer now recognizes patterns such x in {1, 2, 3}
as being a test for membership in a set of constants. The optimizer
recasts the set as a frozenset and stores the pre-built constant.
Now that the speed penalty is gone, it is practical to start writing
membership tests using set-notation.
You can shorten your code by using in:
return x in ("A", "B", "C")
Or, if x is a single character:
return x in "ABC"
If your checks are more complicated than membership in a set and you want to reduce the number of lines of code, you can use conditional expressions:
return True if x == 'A' else True if x == 'B' else True if x == 'C' else False
If you need to apply the same test for many items in a sequence, you may want to use the any function:
return any(x == c for c in "ABC")
This will work for tests that are more complex that equality (so that in can't handle them) and for sequences that are too long for chained conditional expressions. Consider a "near equality" test for floating point numbers:
return any(abs(x - n) < epsilon for n in big_sequence_of_floats)
Related
the question is in the title.
I have a dict of keys and each key value is True or False
And I have a list of items.
I want to iterate through the dict, and check if (key in list) == (or is) dict[key]
Which means I want to see if there is a match between the return value I will get from the "in" call and the value in the dict,
for example:
quick_dict = dict()
quick_list = list()
quick_dict['hi'] = True
quick_dict["hello"] = True
quick_dict["bye"] = False
quick_dict["bi"] = False
quick_dict['zi'] = True
quick_dict["zv"] = True
quick_list.append("hi")
quick_list.append("bye")
for key in quick_dict:
if (key in quick_list) == quick_dict[key]:
print(key)
Which one should I use in this case? and in general what's the different in this case?
In general, you don't want to test boolean variables with is or ==. Just say if ... or put it in a boolean expression by itself.
You want to test 2 conditions, it seems:
Is the key in both collections
Is the dict[key] True
So, you should just write
if key in quick_list and quick_dict[key]:
# do something
If these lists or dictionaries are "large" you should just use set notation and take then iterate only over the intersection of the 2 sets, which automatically takes care of the first condition and shortens the loop to the intersection of the 2 collections like:
In [4]: quick_set = {1, 3, 5}
In [5]: quick_dict = {1: True, 2: True, 3: False, 4: True}
In [6]: matched_keys = quick_set.intersection(set(quick_dict.keys()))
In [7]: for k in matched_keys:
...: if quick_dict[k] : print(k, quick_dict[k])
...:
1 True
Let's look at the code below:
x, y = True, True
u, v = [True], [True]
print(x is y)
print(u is v)
>>> True
>>> False
When using is, there is an added layer of complexity because you're dealing with how the variables are written into memory. We can see that True is stored in memory once. So x and y are hitting the same piece of memory when we call the variable.
Conversely, when we create lists, Python allocates two different places in the memory, one for each list. This means that after the sample code is run, we have True, [True], and [True] stored.
In your example, True and False are written in memory once, no matter how many times we assign them to a variable.
Variables: x y u v
\/ | |
Memory: True [True] [True]
In Python the True and False (and None too) are called singleton objects. They exist in the process once and only once. Any assignment to these value will always be assigned to the same object. So this means any variable that are assigned to True ARE the same version of True (because there is only one version).
x = True
y = True
x is y
# True
(x is y) is True
# True
Generally, you don't use the either syntax in your question. If you want to check the if a value is True, you just pass it as is:
x = True
if x:
print('hello world')
This is cleaner, simpler, and easier to read than:
x = True
if x == True:
print('i am a computer')
Because you are adding an additional evaluation that does not need to take place. In the above example, Python evaluate x == True to True, then evaluates if True to continue the if block.
Except....
The one exception I have seen is sometime you want code to accept either a string or a boolean value, and make a decision based on what is passed. Matplotlib has a bit of this. In this case you might use x is True.
Here is an example of a version number loosener that accepts string or bools.
def format_version(version, how):
allowed = ('full', 'major', 'minor', 'none', 'true', 'false', True, False)
if how not in allowed
raise ValueError(
"Argument `how` only accepts the following values: {}".format(allowed)
)
n = version.count('.')
if (n == 0) or (how=='full') or (how is True):
return version
if n == 1:
major, minor = version.split('.')
subs = ''
if version.count('.') >= 2:
major, minor, subs = version.split('.', 2)
if how == 'major':
return major + '.*'
if how == 'minor':
if not subs:
return '{0}.{1}'.format(major, minor)
return '{0}.{1}.*'.format(major, minor)
Here are specifically checking if True is passed because a simple boolean check on a string will be true as well. To be clear, this is not a good pattern, it just happens because you want your user to be able to pass a True/False value.
A better way of handling this is to convert to string, and then check the strings. Like this:
def format_version(version, how):
how = str(how).lower()
allowed = ('full', 'major', 'minor', 'none', 'true', 'false')
if how not in allowed
raise ValueError(
"Argument `how` only accepts the following values: {}".format(allowed)
)
n = version.count('.')
if (n == 0) or (how == 'full') or (how == 'true'):
return version
if n == 1:
major, minor = version.split('.')
subs = ''
if version.count('.') >= 2:
major, minor, subs = version.split('.', 2)
if how == 'major':
return major + '.*'
if how == 'minor':
if not subs:
return '{0}.{1}'.format(major, minor)
return '{0}.{1}.*'.format(major, minor)
In Scala I could test if a string has a capital letter like this:
val nameHasUpperCase = name.exists(_.isUpper)
The most comprehensive form in Python I can think of is:
a ='asdFggg'
functools.reduce(lambda x, y: x or y, [c.isupper() for c in a])
->True
Somewhat clumsy. Is there a better way to do this?
The closest to the Scala statement is probably an any(..) statement here:
any(x.isupper() for x in a)
This will work in using a generator: from the moment such element is found, any(..) will stop and return True.
This produces:
>>> a ='asdFggg'
>>> any(x.isupper() for x in a)
True
Or another one with map(..):
any(map(str.isupper,a))
Another way of doing this would be comparing the original string to it being completely lower case:
>>> a ='asdFggg'
>>> a == a.lower()
False
And if you want this to return true, then use != instead of ==
There is also
nameHasUpperCase = bool(re.search(r'[A-Z]', name))
I wrote this bit of unnecessarily complicated code on my way to learning about how to use the in statement to make if statements work better. I have two questions following the code snippet.
answer = ['Yes', 'yes', 'YES']
answer2 = ['No', 'no', 'NO']
ans = raw_input()
for i in range(0, 3):
if ans in answer[i]:
print "Yes!"
elif ans in answer2[i]:
print "No!"
else:
print "Don't know what that means"
First question: I think if n in listname: returns as True or False automatically. Does anyone know if that's the case?
Second question: the above code returns 3 lines that vary depending on if ans is actually in answer or answer2. I tried to eliminate that by replacing the relevant portions like so:
if ans in answer[i] == True:
This had the strange effect of making the code output only the else: statement. So can anyone explain to me the difference between how python interprets if ans in answer[i]: and if ans in answer[i] == True:, please?
To answer your questions in reverse order, the reason why explicitly comparing with True did not work for you is that Python did not interpret the expression they way you expected. The Python parser has special handling of compare expressions so that you can chain them together and get a sensible result, like this:
>>> "a" == "a" == "a"
True
Notice that Python has to treat this whole thing as one operation, because if you split it into two operations either way you don't get the same result:
>>> ("a" == "a") == "a"
False
>>> "a" == ("a" == "a")
False
These behave differently because the part in the parentheses is evaluated first and returns True, but True != "a" so the whole expression returns false.
By rights the above shouldn't actually have any impact on your program at all. Unfortunately, Python handles in via the same mechanism as == so when you chain these together they are interpreted as a sequence like the above, so Python actually evaluates it as follows:
>> "a" in ["a"] == True
False
>>> ("a" in ["a"]) and ("a" == True)
False
It's wacky and arguably counter-intuitive, but that's unfortunately just how it works. To get the behavior you wanted you need to use parentheses to force Python to evaluate the first part separately:
>>> ("a" in ["a"]) == True
True
With all of that said, the == True is redundant because, as you suspected, the expression already returns a boolean and the if statement can just evaluate it as-is.
To return now to your other problem, I believe what you're trying to do is take one line of input and produce one corresponding line of output depending on what the user entered. You can apply the in operator to a string and a list to see if the string is in the list, which allows you to eliminate your for loop altogether:
answer = ['Yes', 'yes', 'YES']
answer2 = ['No', 'no', 'NO']
ans = raw_input()
if ans in answer:
print "Yes!"
elif ans in answer2:
print "No!"
else:
print "Don't know what that means"
This first tests if the input matches any of the strings in answer, then the same for answer2. Of course, you could achieve a similar effect but also support other forms like YeS by simply converting the input to lowercase and comparing it to the lowercase form:
if ans.lower() == "yes":
print "Yes!"
# (and so forth)
As far as the difference between ans in answer[i] and ans in answer[i] == True, simple: Python expands the latter form out to ans in answer[i] and answer[i] == True, which is, of course, False since a string is not equal to True.
A simpler example may help illustrate this:
>>> a = [1, 2, 3]
>>> 2 in a
True
>>> 2 in a == True
False
>>> 2 in a and a == True
False
>>> (2 in a) == True
True
>>> 2 in a == [1, 2, 3]
True
>>> 2 in a and a == [1, 2, 3]
True
Notice how adding parentheses changes the behavior - this is akin to expansions like 1 < x < 5, as opposed to (1 < x) < 5.
As a side note, it is generally considered poor style to explicitly check for True or False - it's much better to simply write if x in y.
def name(x):
return x==('Jenson'or'Amra'or'McCay'or'Spinner'or'Jones')
print(name('Jenson'))
print(name('McCay'))
This is the question:
"Write a function that takes as input a name of a person (e.g.,
“smith”, “jones”, etc.) This function should check to see if the name
is one of the five names of people on the board. The five names are:
“Jenson”,”Amra”, “McCay”,”Spinner”, and “Jones”. If the name input
into the function is one of those five names, the function should
return the Boolean value True, and if it isn’t, the function should
return False. (remember comments with input types, description, and
test cases) Test the function to make sure it works."
It works if I am doing Jenson but it comes out with false if I put in any other name.
Try like this,
def name(x):
return x in ('Jenson', 'Amra' ,'McCay', 'Spinner','Jones')
How about the "long" way:
def check_name(x):
names_to_check = ('Jenson','Amra','McCay','Spinner','Jones')
for i in names_to_check:
if i == x:
return True
return False
Here is what is happening in your code:
x = 'Jenson', since this is what you have passed in.
This line x == ('Jenson' or 'Amra' or 'McCay' or 'Jones') is actually a boolean operation, and the result of it is always Jenson.
Boolean operations check truth values, and a non-empty string in Python is always True. So actually what ('Jenson' or 'Amra' or 'McCay' or 'Jones') is saying is:
"Either Jenson or Amra or McCay or Jones which ever one is True, set the value to that".
Since Jenson is the first item, and its True (that is, its not an empty string), the entire expression is equal to Jenson (which is why it only works when you pass in Jenson).
A simple example:
>>> ('a' or 'b' or 'c')
'a'
>>> ('b' or 'a' or 'c')
'b'
>>> ('' or '' or 'a')
'a'
>>> (0 or 0 or 1)
1
>>> (False or False or True)
True
The last three illustrate the same comparison. I am checking two empty strings and 'a'. Since an empty string is False in Python, the only thing that is "True" is 'a', which is what is returned, just as if I was comparing 0 with 1.
The syntax x==('Jenson' or 'Amra' or 'McCay' or 'Spinner'or'Jones') is wrong.
It should be like Adem says. or maybe
def name(x):
return x=='Jenson' or x== 'Amra' or x == 'McCay' or x == 'Spinner' or x == 'Jones'
I imagine what is happening is that ('Jenson'or'Amra'or'McCay'or'Spinner'or'Jones') is being evaluated first, and is evaluated to 'Jenson'. That is computed before x is even considered because it is in parentheses. Then x is checked for equality against Jenson. You need to either use a more advanced syntax like x in... as in Adem's answer, or else use return x == 'Jenson' or x == 'Amra' or x == 'McCay'... so that each comparison is run one after another.
In what order are the contents of the expression"word" in [] == False parsed? It seems to defy all logic:
>>> "word" in [] == False
False
>>> ("word" in []) == False
True
>>> "word" in ([] == False)
TypeError
How does Python actually interpret this expression, and why does it interpret it so?
Edit:
the most general case seems to be
>>> any_value in any_list == any_symbol_or_value
False
Normally you can find out operator precedence with this table..
But this is actually a tricky example:
Comparisons can be chained
arbitrarily, e.g., x < y <= z is
equivalent to x < y and y <= z, except
that y is evaluated only once (but in
both cases z is not evaluated at all
when x < y is found to be false).
See the notes on comparisons.
So "word" in [] == False is really
("word" in []) and ([] == False)
where the two [] are actually the same object.
This is useful for other comparisons, ie 0 < a < 10, but really confusing here!
word in [] is short-circuiting the first expression due to an implicit and. When it realizes that it's false, it stops evaluating the rest of the expression, which is a comparison between [] (the same entity that word was just testing against) and the value False. [] does not equal False, so the implicit "and" expression is false.
("word" in []) == False works as expected because ( ) makes the sub-clause finish and have its result compared to False.
Of course, once again, [] does not equal False.
To clarify the first case, you might test it like this:
foo = []
if ("word" in foo) and (foo == False):
print "True"
else:
print "False"
This is, functionally, what's going on.