How to create a list of conditions? - python

I am trying to create an if else code where there is about 20conditions for the elif, how do I create a list of conditions where i can just type something such as:
uno= <9
lol= >20
crad= =<3
list={uno,lol,crad}
if 13=list:
print("yay")
elif 13!=list:
print("nay")
That's my current code
It should print "yay", instead there is syntax error

It's not actually simpler than writing a chain of if/elif/elif etc, but something like this seems to do what you are asking:
predicates = [lambda x: x<9, lambda x: x>20, lambda x: x<=3]
if all(y(13) for y in predicates):
print("yay")
else:
print("nay")
Each predicate is a small anonymous function (a lambda) which receives a single argument and evaluates to either True or False. If you have a large number of arguments you want to check against a large set of predicates, it's nice to be able to encapsulate the predicates like this. The ability to programmatically add or remove predicates from the list truly extends the versatility of this construct beyond what you can (easily, naturally) do with if/elif/elif.
This particular set of predicates can't all be true for a single number. Maybe you want any() instead of all()...?

Your "conditions" are functions mapping the input to booleans. Therefore, you can write them as functions:
def is_small(number):
return number < 9
def is_large(number):
return number > 20
conditions = (is_small, is_large)
Then you can evaluate all of these functions on some input:
def check_all(input, conditions):
return [condition(input) for condition in conditions]
check_all(10, conditions)
>>> [False, False]
And if you want to know if all of these or any one of these are true, you can use the functions all and any:
any(check_all(10, conditions))
>>> False
any(check_all(21, conditions))
>>> True
And not all(…) is True if one of the conditions is not fulfilled, not any is True if none is.
Edit: One thing to notice is that the list comprehension [… for … in …] in check_all always evaluates all functions. This is not necessary if you use any or all, which can use an iterator and stop evaluating it onces the result it fixed (at the first True for any and the first False for all). If you use them, you can just replace the list comprehension [… for … in …] by a generator expression (… for … in …).

You could create a tuple of booleans and check it via all.
conditions = []
conditions.append(yourVar < 9)
conditions.append(yourVar > 20)
conditions.append(yourVar <= 3)
if all(conditions):
print('All conditions are met')
else:
print('At least one condition results in false)

Related

Python: `and` operator does not return a boolean value

In Python, an empty list is considered a Falsey value
Therefore this is how things should work:
>>> [] and False
False
But in reality, python returns an empty list.
>>> [] and False
[]
Is this intended or a bug?
It's intended. Both and and or are defined to return the last thing evaluated (based on short-circuiting), not actually True or False. For and, this means it returns the first falsy value, if any, and the last value (regardless of truthiness) if all the others are truthy.
It was especially useful back before the conditional expression was added, as it let you do some almost-equivalent hacks, e.g. before the conditional expression:
b if a else c
could be written as:
a and b or c
and, assuming b itself was some truthy thing, it would behave equivalently (the conditional expression lacked that limitation and was more clear about intent, which is why it was added). Even today this feature is occasionally useful for replacing all falsy values with some more specifically-typed default, e.g. when lst might be passed as None or a list, you can ensure it's a list with:
lst = lst or []
to cheaply replace None (and any other falsy thing) with a new empty list.
This is how it is supposed to work. and will only return the right hand operand if the left hand operand is truthy. Since [] is falsy, and returns the left hand operand.
That's a totally expected behaviour. To understand it, you need to know how the Boolean operators (and, or, not) work. From the Boolean Operations documentation:
The expression x and y first evaluates x; if x is false, its value is returned; otherwise, y is evaluated and the resulting value is returned.
Now let's consider your example: [] and False. Here, since [] is falsey, it's value is returned back by the statement which is [].
Above linked Python documentation explicitly mentions:
Note: Neither and nor or restrict the value and type they return to False and True, but rather return the last evaluated argument.
However, in case you need the return value as boolean, you can explicitly type-cast the value to True or False using the bool() function.
For example, in your case it will return as False:
>>> bool([] and False)
False

I don't fully understand how the any() function in this script works

I have a script that checks if there are one or more of the same items in a list. Here's the code:
items = ["Blue", "Black", "Red"]
def isUnique(item):
seen = list()
return not any(i in seen or seen.append(i) for i in item)
print(isUnique(items))
It prints "True" if all the items in the given list are unique and "False" if one or more items in the list are unique. Can someone please explain the any() part of the script for me as I don't fully understand how it works?
This code is kind of a hack, since it uses a generator expression with side-effects and exploits the fact that append returns None, which is falsy.
The equivalent code written in the imperative style is like so:
def isUnique(items):
seen = list()
for i in items:
if i in seen or seen.append(i):
return False
return True
The or is still a bit strange there - it is being used for its short-circuiting behaviour, so that append is only called when i in seen is false - so we could rewrite it like this:
def isUnique(items):
seen = list()
for i in items:
if i in seen:
return False
else:
seen.append(i)
return True
This is equivalent because append is only called when i in seen is false, and the call to append returns None which means the return False line shouldn't execute in that case.
Here you need to understand first how or operator works.
or is like exp1 or exp2
it just evaluates the expression which gives True first or give true at last
eg
>>> 2 or 3
2
>>> 5 or 0.0
5
>>> [] or 3
3
>>> 0 or {}
{}
now for your list comprehension, [i in seen or seen.append(i) for i in items] i in seen evaluate false and seen.append(i) True and which return None ie list.append return None so , comprehension contain all None
>>> seen = []
>>> items = ["Blue", "Black", "Red"]
>>> res = [i in seen or seen.append(i) for i in items]
>>> res
[None, None, None]
>>> any(res)
False
as per any documentation, it is returning false beacuse as it is not getting iterable or bool.
>>> help(any)
Help on built-in function any in module builtins:
any(iterable, /)
Return True if bool(x) is True for any x in the iterable.
If the iterable is empty, return False.
the any function in python takes a list of booleans and returns the OR of all of them.
the i in seen or seen.append(i) for i in item appends i to seen if it's not in seen already. but if it is already in seen then the append() does not run since the first part is already True, and python doesn't need to know if the second part is true since True OR'd with anything is True. so it doesn't execute it. so the seen array ends up being a unique list of colours it has seen.
i in seen or seen.append(i) for i in item is also a generator expression,
which generates booleans, and any checks the booleans it generates, if even one of them evaluates to True, the whole any will return True.
so the first time an item that is already in the seen array is found, any will stop the generator and return True itself.
so if a duplicate element happens to be in the array no more conditions are evaluated and no more elements are appended to seen array
so if the array had duplicate elements, like,
items = ["Blue", "Blue", "Black", "Red"]
def isUnique(item):
seen = list()
unique = not any(i in seen or seen.append(i) for i in item)
print(seen)
return unique
isUnique(items)
would result in the output, just
['Blue']
EDIT: there are great answers. Adding some simpler ways to achieve the wanted result:
Method 1:
items = ["Blue", "Black", "Red"]
items_set = set(items)
if len(items_set) != len(items):
# there are duplications
This works because a set object ‘removes’ duplications.
Method 2:
contains_duplicates = any(items.count(element) > 1 for element in items) # true if contains duplications and false otherwise.
See https://www.kite.com/python/answers/how-to-check-for-duplicates-in-a-list-in-python
———————————————
any is a great function
Return True if any element of the iterable is true. If the iterable is empty, return False
Your function isUnique, however, does a bit more logic. Let's break it down:
First you create an empty list object and store it in 'seen' variable.
for i in item - iterates the list of items.
i in seen - This statement returns True if 'i' is a member of 'seen', and false otherwise.
seen.append(i) - add i to seen. This statement returns None if 'i' is appeneded to seen successfully.
Notice the or statement between i in seen or seen.append(i). That means, if one of the statements here is True, the or statement returns True.
At this point, I'd run [i in seen or seen.append(i) for i in item], see the result and experiment with it. The result for your example is [None, None, None].
Basically, for each item, you both add it to the list and check if it is already in the list.
Finally, you use the any() function - which returns True if the iterable has a True value. This will happen only if i in seen will return True.
Notice you are using not any(...), which returns False in case there are no repititions.
There are simpler and clearer ways to implement this. You should try!
It is quite simple: the expression inside any() is a generator. any() draws from that generator and returns True (and stops) at the first element from the generator that is True. If it exhausts the generator, then it returns False.
The expression in the generator (i in seen or seen.append(i)) is a trick to express as a one-liner the logic that: if i is in the list, the expression is True and any() stops immediately, otherwise, i is added to the list and the generator continues.
The function can be significantly improved by using a set instead of a list:
def isUnique(item):
seen = set()
return not any(i in seen or seen.add(i) for i in item)
It is much faster to test for presence of an item in a set (O[1]) than in a list (O[n]).
One interesting and perhaps underappreciated aspect of this code is that it works on a (potentially infinite) generator. It will stop drawing from the generator at the first repeated item. Subsequent items that would be obtained by the generator are not evaluated at all (with potential side-effects, desirable or not).
A different approach, suitable for known and finite collections of items, would be the following:
def isUnique(items):
items = tuple(items) # in case items is a generator
return len(set(items)) == len(items)
This assumes that all the items fit in memory. Obviously this won't work if items is a generator of a very large or infinite number of elements.

Boolean evaluation of a string without conditionals

I'm trying to figure out the solution to this particular challenge and so far I'm stumped.
Basically, what I'm looking to do is:
Check if substring false exists in string s
If false exists in s, return the boolean True
However, I am not allowed to use any conditional statements at all.
Maybe there is a way to do this with objects?
There is always str.__contains__ if it's needed as a function somewhere:
In [69]: str.__contains__('**foo**', 'foo')
Out[69]: True
This could be used for things like filter or map:
sorted(some_list, key=partial(str.__contains__,'**foo**'))
The much more common usecase is to assign a truth value for each element in a list using a comprehension. Then we can make use of the in keyword in Python:
In[70]: ['foo' in x for x in ['**foo**','abc']]
Out[70]: [True, False]
The latter should always be preferred. There are edge cases where only a function might be possible.
But even then you could pass a lambda and use the statement:
sorted(some_list, key=lambda x: 'foo' in x)
Evaluating condition without using if statement:
True:
>>> s = 'abcdefalse'
>>> 'false' in s
True
False:
>>> s = 'abcdefals'
>>> 'false' in s
False
Return blank if False:
>>> s = 'abcdefals'
>>> 'false' in s or ''
''

how to use python's any

I feel very confused about some code like this[not written by me]:
version = any(func1(), func2()) # wrong, should be any([func1(), func2()])
def func1():
if something:
return 1
else:
return None
def func2():
if something:
return 2
else:
return 3
version must be a num. when [func1(), func2()] is [1, None], should return 1, when is [None, 2], should return 2, when [1, 2], should return 1.
so I think it's wrong to use any() in this code, because any() just return True or False. If I rewirte this logic using another way, I can not find a graceful way as a pythoner.
I want to know whether any() can achieve the logic, if not, how to achieve it gracefully?
You can simply use or here.
version = func1() or func2()
Make sure the functions are defined before trying to call them.
This works because or returns the first True-like value or the last value (if no value is True-like) . And 'None' is considered False-like in Boolean context.
#AnandSKumar's answer is optimal. But just to give you some information on any if you are interested:
Take this example:
>>> def foo():
... return 2
...
>>> def boo():
... return 3
...
>>> def doo():
... return 4
...
>>> f = [foo, boo, doo]
>>> any(i() < 3 for i in f)
True
Ultimately what is happening inside the any is, iterate over the array of methods j, and indicate whether each item is less than 3, what the "any" will do in this case is return "ANY" condition that matches that. So even if you find one that is False, it will still return True.
There is another similar method to this called "all", that will ensure that ALL conditions are met based on your condition you are checking. Here is the example:
>>> all(i() < 3 for i in f)
False
So, as you can see, because one condition failed, it will return False.
For the arbitrary length case (where explicitly chaining or doesn't make sense), you can make a version of any that returns the first truthy value or a given value when all results are falsy with:
# If on Py2, you'll want to do this to get shortcircuiting behavior
from future_builtins import filter
result = next(filter(None, iterable_to_check), False) # False can be replaced with a chosen default
The filter only produces "truthy" values, and the two arg next gets the first "truthy" value, or the default value if filter finds no "truthy" values.
If the iterable_to_check is a non-empty Sequence (rather than merely Iterable), you can exactly match the behavior of chained ors (the "falsy" result is the last value, not a specific value like False or None) with:
result = next(filter(None, sequence_to_check), sequence_to_check[-1])
to use the final element ("truthy" or "falsy") as the result when all elements were "falsy".
To be clear, if the set of things to test is fixed and smallish, explicit or per Anand's answer is the better way to go.

Python - function to do string comparison

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.

Categories

Resources