If all elements match requirement not using "if all" - python

I am learning python with a book: The exercise is to make a program that print True if all numbers in a list all odds.
I get it whit this approach
if all(x % 2 == 1 for x in list):
But the 'if all' approach is not yet explained. They only use while, if,for and booleans in the examples. Furthermore, seems to be a reflexive exercise about it is possible to do it, maybe not. It is possible to do it using the basic tools of above?

If you look at the docs: https://docs.python.org/3/library/functions.html#all
all(iterable) .
Return True if all elements of the iterable are true (or if the iterable is empty). Equivalent to:
def all(iterable):
for element in iterable:
if not element:
return False
return True
So if all(x % 2 == 1 for x in li): roughly translates to
def are_all_odds(num_list):
#Flag to keep track if we encounter an even number
flag = True
for num in num_list:
#Once we encounter a even number, we break the for loop
if num % 2 != 1:
flag = False
break
#Return the flag
return flag
We can test this function by doing
print(are_all_odds([1, 2, 3, 4]))
#False
print(are_all_odds([1, 3, 5]))
#True
Also just a suggestion, list is a python builtin keyword, so don't use it in variables :)

Yes, it is possible.
The Python code you wrote is very idiomatic, keep up that good work.
To see how to do it differently, you can look at programming languages that are less advanced, such as C. That is a very basic programming language which lacks the features for this if all statement. Searching for "c all elements array true" should give you the code you are looking for. For such a simple piece of code, it's easy to translate the code back into Python.

Related

Comparing nested structures of arrays in Python

Good evening everyone,
I am comparing nested structures of two arrays of the same length (i.e.: [ 1, [ 1, 1 ] ], [ [ 2, 2 ], 2 ] ) by checking for the type and length equality as follows:
def array_structure(array1, array2):
for i in range(len(array1)):
if type(array1[i]) == type(array2[i]):
if isinstance(array1[i], int) == False:
if len(array1[i]) == len(array2[i]):
return True
else:
return False
elif len(str(array1[i])) == len(str(array2[i])):
return True
else:
return False
else:
return False
The function should return True or False whether the nested structures of the arrays are equal or not.
My attemp works for some arrays, but I'm feeling like I'm missing the point so I would appreciate any help to understand and find a more pythonic way to code this and improve the logical approach.
You can use itertools.zip_longest and all:
from itertools import zip_longest
def compare(a, b):
if not isinstance(a, list) or not isinstance(b, list):
return a == b
return all(compare(j, k) for j, k in zip_longest(a, b))
print(compare([[1, [2, [3, 4]]]], [[1, [2, [3, 4]]]]))
print(compare([3, [4], 5], [3, [6], 5]))
Output:
True
False
Note that you are manipulating lists, not arrays.
The exact meaning of "structure" is a bit vague, I am guessing that you mean that there are either integers or nested lists of the same length, at the same positions.
If that is so, here are some problems:
As soon as you find two identical things, you return and exit. This means that if the first elements are identical, you never compare the second element !
>>> array_structure([1,2],[4,[5]])
True
Finding the "same structure" at one position isn't enough to deduce that it is true for all indices. You need to keep checking. However, you are right that as soon as you find "different structure", you can safely return, because it is enough to make the structures different overall.
When you have the same type for both elements, if you have an integer, then you convert them to strings, then compare lengths, otherwise, you compare the lengths. This looks strange to me: Why would you say that [1] and [11] have different structures ?
>>> array_structure([1],[11])
False
What about nested arrays ? maybe array_structure([1,[2]],[1,[2,[3,4,5,6]]]) should be false, and array_structure([4,[5,[8,3,0,1]]],[1,[2,[3,4,5,6]]]) should be true ? If you have seen the notion of recursion, this might be a good place to use it.
A good strategy for your problem would be to go over each element, make some tests to check if you have "different structures", and return False if that is the case. Otherwise, don't return, so you can go over all elements. This means that if anything is different, you will have exited during iteration, so you will only reach the end of the loop if everything was identical. At that point, you can return True. The bulk of the function should look like:
for i in range(<length>):
if <different structure>:
return False
return True
More tips for later:
Testing for types in general is not very pythonic. Unless the exercise tells you that you will only ever get integers or lists, you could have other things in these arrays that are not integers and don't have a length. Rather than testing whether the things are ints to deduce if they have a length, you could test for hasattr(array1[i], 'len'). This will be True if the element is of a type that has a length, and False otherwise.
Each time you have something that looks like if <condition>: return True else return False you can be sure it is identical to return <condition>. This helps in simplifying code, but in your example, it's not a great idea to change it right away, because you should not be returning when you find True to your conditions.
Each time you have something that looks like <condition> == False, it is better and more readable to write not <condition>. It is also possible to switch the logic over so that you have a positive condition first, which is often more readable.

Python any() function does not act as expected for list of negative numbers

I am trying to compare a list of numbers in an if statement with the any() function. I am using python 3.6 in Spyder. The code in question is:
if any(lst) >= 1:
do_some_stuff
lst is actually generated by list(map(my_func, other_lst)) but after diagnosing my problem I get two behaviors as shown below with my actual lst passed to the any() function:
any([1.535, 1.535, 1.535]) >= 1
>>True
Which is expected.
any([-0.676, -0.676, -0.676]) >= 1
>>True
Which is not expected.
I've delved deeper and found that any number I can put in lst that is less than 0 yields True in my if statement. Also, converting the 1 to a float doesn't help. After reading "truth value testing", this post, and extensive time trying to learn the behavior within my program, I am at a loss. Please help, I am pretty new to python. Thanks!
You are comparing the wrong thing. You are comparing the result of any to 1.
any(mylist) will return true if any element of the list is nonzero. True is greater than or equal to 1.
So this
any(mylist)>=1
is equivalent to just
any(mylist)
What you mean is
any(x>=1 for x in mylist)
Please read the documentation:
any(iterable)
Return True if any element of the iterable is true. If
the iterable is empty, return False. Equivalent to:
def any(iterable):
for element in iterable:
if element:
return True
return False
All your any() expression listed will return True, and you are comparing True with 1, that is True.

python if/else logic explained please

I have a really basic if/else logic question for Python. An exercise in the book required me to write a function that takes a list of integers are returns True if the list contains all even numbers and False if it doesn't.
I wrote:
list1 = [8,0,-2,4,-6,10]
list2 = [8,0,-1,4,-6,10]
def allEven(list):
for x in list:
if x % 2 != 0:
return False
else:
return True
This code always returns True. Why is that? Doesn't the code see the -1 during the loop of all the values of the list and returns the False?
list1 = [8,0,-2,4,-6,10]
list2 = [8,0,-1,4,-6,10]
def allEven(list):
for x in list:
if x % 2 != 0:
return False
return True
The book gives the answer of this. Why does this work and mine doesn't?
Pay close attention to where that else is placed. Indentation and nesting matters here!
In your first example, it will return True on the first element that satisfies your condition because your first if check fails.
In your second example, it will return True after all elements have been iterated through and a return value hasn't been produced.
The first function checks the first number only, since it returns something as soon as the for loop starts.
By the way, you can but should not use list as an argument or a variable name, since it is a keyword.
I strongly recommend writing a print statement to output x before both of your return statements. It will help you understand the flow of the code.
The short answer is that only the first element is being checked by your code, and the function returns True or False based on that value.
In the book solution, any failure causes a return of False, but the loop simply continues otherwise. Only if all elements are checked without failure does the return True reached.

Best way to do ternary conditionals in Python < 2.5

I have to bear with a Python version < 2.5 (it is 2.4.3 for specifics)
It seems that ternary operators were introduces in Python starting at 2.5. For those who are not familiar, ternary operators in Python >= 2.5 look like this:
def do_ternary(flag):
return "foo" if flag else "bar"
I'd like to know some solutions to emulate this in the early versions of Python. I can for sure do it with if ... else, but I'm looking for something more pythonic that I wouldn't be ashamed to put on some production-level code :)
Thanks for the help !
the correct way that does all of the things that if/else does is:
(condition and (yes_value,) or (no_value,))[0]
which does both the short circuiting and resolves the problem of when yes_value is itself falsey. Obviously, if you have reason to avoid this cludge, just do that; in your example, both conditions are constant expressions, so you can do:
{True: yes_value, False: no_value}[bool(condition)]
or more tersely:
(no_value, yes_value)[condition]
if you do need the short circut, but you're confident that the yes_value is never falsey, you can trim out the tuple:
condition and yes_value or no_value
but that's probably only valid when the yes_value is actually a constant. If none of these suit your tastes or needs, just use a plain-ol if: statement, with an intermediate variable
if condition:
result = yes_value
else:
result = no_value
Actually I was looking on the web and found what seems like a really elegant pythonic solution:
def _if(test):
return lambda alternative: \
lambda result: \
[delay(result), delay(alternative)][not not test]()
def delay(f):
if callable(f): return f
else: return lambda: f
>>> fact = lambda n: _if (n <= 1) (1) (lambda: n * fact(n-1))
>>> fact(100)
93326215443944152681699238856266700490715968264381621468592963895217599993229915608941463976156518286253697920827223758251185210916864000000000000000000000000L
What do you think about this one? It looks pretty clean and easy to read in my opinion.
A common trick is to use list-indexing, since False/True turn into 0/1 when an integer is required. In case the test may be false-y or truth-y rather than a boolean, its good practice to first ensure the test is a boolean:
["bar", "foo"][bool(flag)]
will produce the same output as the ternary in your question.
Edit: Dougal points out that this may behave slightly differently from the ternary, because both the true and false values will be evaluated, which may have side-effects.
The classic 'trick' used to do this is:
test and true_value or false_value
This works as and and or work like so in python:
x or y -> if x is false, then y, else x
x and y -> if x is false, then x, else y
Source
This means that we get the roughly the same result - so long as true_value evaluates to True - so, for example, the following would not work:
flag and [] or "bar"
As [] evaluates to False.
I'd still argue that this is less readable than simply using an if/else block, as unless you are familiar with it, it's unclear.
So I'd advise using:
if test:
return true_value
else:
return false_value
(replacing return with assignment or whatever where needed).

How do "and" and "or" work when combined in one statement? [duplicate]

This question already has answers here:
Does Python have a ternary conditional operator?
(31 answers)
How do "and" and "or" act with non-boolean values?
(8 answers)
Closed 4 months ago.
For some reason this function confused me:
def protocol(port):
return port == "443" and "https://" or "http://"
Can somebody explain the order of what's happening behind the scenes to make this work the way it does.
I understood it as this until I tried it:
Either A)
def protocol(port):
if port == "443":
if bool("https://"):
return True
elif bool("http://"):
return True
return False
Or B)
def protocol(port):
if port == "443":
return True + "https://"
else:
return True + "http://"
Is this some sort of special case in Python, or am I completely misunderstanding how statements work?
It's an old-ish idiom; inserting parentheses to show priority,
(port == "443" and "https://") or "http://"
x and y returns y if x is truish, x if x is falsish; a or b, vice versa, returns a if it's truish, otherwise b.
So if port == "443" is true, this returns the RHS of the and, i.e., "https://". Otherwise, the and is false, so the or gets into play and returns `"http://", its RHS.
In modern Python, a better way to do translate this old-ish idiom is:
"https://" if port == "443" else "http://"
and returns the right operand if the left is true. or returns the right operand if the left is false. Otherwise they both return the left operand. They are said to coalesce.
C and X or Y is the long-running early attempt by Python users to proxy for C ? X : Y
For the most part it works, except if X is False -- this has led to many bugs in Python code, so in the Python FAQ, you'll find the more correct solution being (C and [X] or [Y])[0] because a list with a single element, regardless of its evaluated Boolean value, is always True! For example: [None] is True but None isn't. The OP's example above works because the string representing X is not empty.
However, all this changed in Python 2.5, when the ternary or conditional operator was added to the language, allowing you to use the cleaner X if C else Y as stated in other posts here. If you see code using the older format, it's because the user has been a long time Python programmer who hasn't adopted the new syntax yet, they cut-n-paste other old code, or their employer is still using 2.4.x (or earlier releases), etc.
This is an ugly hack that is not recommended. It works because of the short-circuiting behaviour of and and or and that they return the one of their arguments rather than a boolean value. Using this technique gives a risk of introducing hard-to-find bugs, so don't use it in new code.
Here's an example of how the and/or idiom can give an unexpected result:
>>> foo = 'foobar'
>>> bar = 'foobar'
>>> x = 0
>>> y = 1
>>> (foo == bar) and x or y # Will this return the value of x if (foo == bar)?
1
Prefer instead the newer notation:
return "https://" if port == "443" else "http://"
You may want to read up on the "and / or trick" of Python in this article The Peculiar Nature of And and Or in Python. It's a bit like the IIF() in VBA or VB, or ?: in C-style languages.
This construction works because it 'unfolds' to the following code:
a and b -->
if a:
return b
else:
return a
a or b -->
if a:
return a
else:
return b
With all the good answers, I found these statements help me remember this better and fit how my brain works (and hopefully for for some more out there) :
“and" returns the first False item (e.g., None, “”, [], (), {}, 0) or the last item if none (e.g. no False found)
“or" returns the first True item or the last item (e.g. no True found)**
In summary:
they all return the first item that decides the outcome of the statement. (In the worst case, the last item in the sequence)
Note this rule also applies to a chained all "and" or all "or" statement

Categories

Resources