Easier way to write conditional statement - python

Is there any prettier way to write this if-statement:
if not (self.is_legal(l) or self.is_legal(u) or self.is_legal(r) or self.is_legal(d)):
I've tried this, but it didn't work.
if not self.is_legal(l or r or d or u):
Or maybe the first one is the prettiest?

You can use any and a generator expression:
if not any(self.is_legal(x) for x in (l, u, r, d)):
Or, if you prefer all instead of any:
if all(not self.is_legal(x) for x in (l, u, r, d)):
The first solution seems to read a little better though.
As for why your attempted solution did not work, the or operator in Python does not behave as you think it does. From the docs:
The expression x or y first evaluates x; if x is true, its value
is returned; otherwise, y is evaluated and the resulting value is
returned.
So, self.is_legal(l or r or d or u) was only passing the first truthy value to the self.is_legal method, not all of them.

An empty list in Python is false.
You can produce an empty list with a comprehension and a conditional like so:
>>> def is_legal(x):
... return x>5
...
>>> bool([x for x in (1,2,3,4) if is_legal(x)])
False
>>> bool([x for x in (1,2,3,4,6) if is_legal(x)])
True

You can use a tuple or set:
if False in (self.is_legal(l), self.is_legal(u), self.is_legal(r), self.is_legal(d)):
if {False} <= {self.is_legal(l), self.is_legal(u), self.is_legal(r)}:
Extending this idea a bit further:
if {False} <= {self.is_legal(var) for var in (l, u, r, d)}:
Or using the fact that an empty list or set or whatever is false:
if [var for var in (l, u, r, d) if self.is_legal(var) is False]:
I guess it all depends on what you consider to be "pretty".

Related

Unpack a list with ternary operator

I stumbled upon a weird behaviour when I want to unpack a list only if condition is true.
How can I use the unpacking (*) based on a condition?
Example:
def foo1(x1, x2):
print(x1, x2)
def foo2(x):
print(x)
l = [7,8]
foo1(*l if True else l) # this works
foo2(*l if False else l) # this does not work
foo1(l if not True else *l) # this does not work
That's not parsed as choosing between *l on the left and l on the right. l if True else l is evaluated, and then the result is unpacked, unconditionally.
There's no way to have a function call expression conditionally either unpack or not unpack an argument. Something's getting unpacked if you put a * there. What you can do is wrap l in another list and unpack that:
foo(*(l if condition else [l]))

How to check if a number in a list range

I have a float number x and a list range list_ = [[a, b], [c,d], [e,f]]
How can check if the number x is in the list. It means the function will return True in case of
a<=x <=b
or
c<=x <=d
or
e<=x <=f
Otherwise, the function will return False. Could you help me to write a Python code for the function
function (x, list_)--> True/False
Clean solution:
def function(x, list_):
return any([l[0] < x < l[1] for l in list_])
Optimized solution:
def function(x, list_):
for l in list_:
if l[0] < x < l[1]:
return True
return False
The idiomatic solution would be this:
def f(x: int, ls: List[Tuple[float, float]]) -> bool:
return any(a <= x <=b for (a, b) in ls)
Take specific note of the following:
Naming a function function is a super poor idea.
It is abnormal and therefore a poor idea to name a variable list_ just to avoid overriding a keyword.
Using the form any ensures that you quickly quit when you find a valid solution.
You can quickly destructure your tuple (or list, if you happen to pass a list) using the for (a, b) in ls clause.
This solution is as quick as if you use a for clause, but all of that is premature optimization anyway.
Using an explicit destructing ensures you have two and only two elements for your sublist.
It was requested that I check certain inputs:
>>> f(10.1, [[8.1, 12.1], [110, 120]])
True
Seems to work!
If you're running into NameError, the issue is simply one of the importation of types. You can either define f like so:
def f(x, ls):
... // As you would otherwise
Or import the required types to make the type-hinting work properly:
from typing import List, Tuple
def f(x: int, ls: List[Tuple[float, float]]) -> bool:
... // As you would otherwise
This has little to do with the original question or solution - it's just standard for type hinting in python.
def function(x,list__):
for [a,b] in list_data:
if a<=x<=b:
return True
return False
You can simply iterate through the list and find whether it's in range or not.
I'm generating the variable and the list randomly and calling a function that iterates and checks whether the variable lies within the range of any of the members of the list.
import numpy as np
def find_if_in_range(list_, var) -> bool:
for i in list_:
if i[0] <= var <= i[-1]:
return True
return False
var = np.random.randint(10)
list_ = np.random.randint(10, size=(3,2), dtype=np.uint8)
print(f"var: {var}")
print(f"list: {list_}")
res = find_if_in_range(list_, var)
print(res)
Output:
var: 0
list: [[0 6]
[2 7]
[7 9]]
True
Hope this helps.
Cheers.

how to get the variable from an any() function

I am looking to get the list_select variable that meets the criteria and do an append in the next line.
How can I make this available in the list_select.append(dupe) line?
if any(list_select in dupe for list_select in pattern_dict):
list_select.append(dupe)
You can't with any, which just returns a boolean. Use a generator expression instead:
gen = (x for x in pattern_dict if x in dupe)
list_select = next(gen, None)
if list_select is not None:
...
The any function is supposed to return a boolean, so if you want the slightly different behaviour of returning the first match, write a function with that behaviour:
def first(seq):
return next(iter(seq), None)
Usage:
>>> first( i for i in range(10) if i**2 > 10 )
4
>>> first( c for c in 'Hello, world!' if c.islower() )
'e'
>>> first( i for i in range(10) if i == 100 ) is None
True
To use in your example, you would write something like:
list_select = first( x for x in pattern_dict if x in dupe )
if list_select is not None:
list_select.append(dupe)
If you are using Python 3.8 or later, the dreaded "walrus" operator allows for a more direct solution:
if any((list_select := x) in dupe for x in pattern_dict):
list_select.append(dupe)
This situation happens to be one of the motivating examples for introducing the walrus operator.

Python: general iterator or pure function for testing any condition across list

I would like to have a function AllTrue that takes three arguments:
List: a list of values
Function: a function to apply to all values
Condition: something to test against the function's output
and return a boolean of whether or not all values in the list match the criteria.
I can get this to work for basic conditions as follows:
def AllTrue(List, Function = "Boolean", Condition = True):
flag = True
condition = Condition
if Function == "Boolean"
for element in List:
if element != condition:
flag = False
break
else:
Map = map(Function, List)
for m in Map:
if m != condition:
flag = False
break
return flag
Since python doesn't have function meant for explicitly returning if something is True, I just make the default "Boolean". One could clean this up by defining TrueQ to return True if an element is True and then just mapping TrueQ on the List.
The else handles queries like:
l = [[0,1], [2,3,4,5], [6,7], [8,9],[10]]
AllTrue(l, len, 2)
#False
testing if all elements in the list are of length 2. However, it can't handle more complex conditions like >/< or compound conditions like len > 2 and element[0] == 15
How can one do this?
Cleaned up version
def TrueQ(item):
return item == True
def AllTrue(List, Function = TrueQ, Condition = True):
flag = True
condition = Condition
Map = map(Function, List)
for m in Map:
if m != condition:
flag = False
break
return flag
and then just call AllTrue(List,TrueQ)
Python already has built-in the machinery you are trying to build. For example to check if all numbers in a list are even the code could be:
if all(x%2==0 for x in L):
...
if you want to check that all values are "truthy" the code is even simpler:
if all(L):
...
Note that in the first version the code is also "short-circuited", in other words the evaluation stops as soon as the result is known. In:
if all(price(x) > 100 for x in stocks):
...
the function price will be called until the first stock is found with a lower or equal price value. At that point the search will stop because the result is known to be False.
To check that all lengths are 2 in the list L the code is simply:
if all(len(x) == 2 for x in L):
...
i.e. more or less a literal translation of the request. No need to write a function for that.
If this kind of test is a "filter" that you want to pass as a parameter to another function then a lambda may turn out useful:
def search_DB(test):
for record in database:
if test(record):
result.append(record)
...
search_DB(lambda rec: all(len(x) == 2 for x in rec.strings))
I want a function that takes a list, a function, and a condition, and tells me if every element in the list matches the condition. i.e. foo(List, Len, >2)
In Python >2 is written lambda x : x>2.
There is (unfortunately) no metaprogramming facility in Python that would allow to write just >2 or things like ยท>2 except using a string literal evaluation with eval and you don't want to do that. Even the standard Python library tried going down that path (see namedtuple implementation in collections) but it's really ugly.
I'm not saying that writing >2 would be a good idea, but that it would be nice to have a way to do that in case it was a good idea. Unfortunately to have decent metaprogramming abilities you need a homoiconic language representing code as data and therefore you would be programming in Lisp or another meta-language, not Python (programming in Lisp would indeed be a good idea, but for reasons unknown to me that approach is still unpopular).
Given that, the function foo to be called like
foo(L, len, lambda x : x > 2)
is just
def foo(L, f=lambda x : x, condition=lambda x: x):
return all(condition(f(x)) for x in L)
but no Python programmer would write such a function, because the original call to foo is actually more code and less clear than inlining it with:
all(len(x) > 2 for x in L)
and requires you to also learn about this thing foo (that does what all and a generator expression would do, just slower, with more code and more obfuscated).
You are reinventing the wheel. Just use something like this:
>>> l = [[0,1], [2,3,4,5], [6,7], [8,9],[10]]
>>> def all_true(iterable, f, condition):
... return all(condition(f(e)) for e in iterable)
...
>>> def cond(x): return x == 2
...
>>> all_true(l, len, cond)
False
You can define a different function to check a different condition:
>>> def cond(x): return x >= 1
...
>>> all_true(l, len, b)
True
>>>
And really, having your own function that does this seems like overkill. For example, to deal with your "complex condition" you could simply do something like:
>>> l = [[0,2],[0,1,2],[0,1,3,4]]
>>> all(len(sub) > 2 and sub[0] == 5 for sub in l)
False
>>> all(len(sub) > 1 and sub[0] == 0 for sub in l)
True
>>>
I think the ideal solution in this case may be:
def AllTrue(List, Test = lambda x:x):
all(Test(x) for x in List)
This thereby allows complex queries like:
l = [[0, 1], [1, 2, 3], [2, 5]]
AllTrue(l, lambda x: len(x) > 2 and x[0] == 1)
To adhere to Juanpa's suggestion, here it is in python naming conventions and an extension of what I posted in the question now with the ability to handle simple conditions like x > value.
from operator import *
all_true(a_list, a_function, an_operator, a_value):
a_map = map(a_function, a_list)
return all( an_operator(m, a_value) for m in a_map)
l = [[0,2],[0,1,2],[0,1,3,4]]
all_true(l, len, gt, 2)
#True
Note: this works for single conditions, but not for complex conditions like
len > 2 and element[0] == 5

rewrite small piece of python code

I have lots of small pieces of code that look like:
for it in <iterable>:
if <condition>:
return True/False
Is there a way I can rewrite this piece of code with a lambda expression ? I know I can factor it out in a small method/function, but I am looking for some lambda thing if it can be done.
Use the built-in any function.
e.g.
any(<condition> for it in <iterable>) # return True on <condition>
In addition to what everyone else has said, for the reverse case:
for it in <iterable>:
if <condition>:
return False
return True
use all():
b = all(<condition> for it in <iterable>)
if you want to check the condition for every item of iterable you can use
listcomprehensions to to this
b = [ x == whatever for x in a ]
you can combine this with any if you only need to know if there is one element
that evals to true for your condition
b = any(x == whatever for x in a)
Here is a simple example which returns True if any of the objects in it is equal to 2. by using the map function:
any(map(lambda x: x==2, it))
Change the lambda expression to reflect your condition.
Another nice way is to use any with a list comprehension:
any([True for x in it if x==2])
or alternatively, a generator expression:
any(x==2 for x in it)

Categories