I have a simple code that goes like this in Python:
a = [1,2,3]
b = [2,4,6]
def union(a,b):
pos = 0
while pos < len(b):
n = b[pos]
if n in a is not 'True':
a = a
else:
a.append(n)
pos = pos +1
return a
print union(a,b)
As you can see, the first IF statement makes no sense. However, if I code it this way:
if n in a is 'True':
a.append(n)
it does not work. The first code segment changes a = [1,2,4,6] - only adding numbers from list 'b' that are not in list 'a' already. If I change the 'IF' snippet to "is 'True" as suggested, it does not work.
While this function does what I intended it to do, I feel it is not clean and I have no idea why "if n in a is 'True':" would not behave equal to the else part of the "if n in a is not 'True':" function.
Can somebody please help me understand this?
It is not a very pythonic way to use boolean check and then compare it with a string, so it would be better to do it this way:
a = [1,2,3]
b = [2,4,6]
def union(x,y):
for v in y:
if v not in x:
x.append(v)
return x
print union(a,b)
OR:
a.extend(set(b).difference(set(a)))
print a
>>> [1, 2, 3, 4, 6]
OR in case you don't care about new objects creating than:
print list(set(a).union(b))
in and is/is not are both relational operators, and in Python relational operators are chained. Therefore n in a is not 'True' is equivalent to n in a and a is not 'True', and n in a is 'True' is equivalent to n in a and a is 'True'. Clearly these are not negations of each other since they both have n in a.
But don't use is unless you know you need it, and never compare against a boolean either (unless yadda yadda).
You should just use True not the string 'True'
or better yet, just
if n not in a:
a.append(n)
If you are a beginner, you may not realise that Python has a builtin type called set
set objects already have methods for intersection/union etc.
You can use
if n in a
or
if n not in a
instead of the is 'True'.
Related
I have the following funciton telling us that a series has at least one negative value:
def has_negative(series):
v=False
for i in range(len(series)):
if series[i]<0:
v=True
break
return v
When we use this function on an example we get :
y=[1,2,3,4,5,6,7,8,9]
z=[1,-2,3,4,5,6,7,8,9]
print(has_negative(y))
print(has_negative(y))
Output:
>>> False
>>> True
The function seems to work well, although I want to make it shorter, any suggestion from your side will be appreciated
You can utilise the built-in any function as follows:
def has_negative(lst):
return any(e < 0 for e in lst)
print(has_negative([1,2,3,4,5,6,7,8,9]))
print(has_negative([1,-2,3,4,5,6,7,8,9]))
Output:
False
True
EDIT:
Did some timing tests based around this and other suggested answers. Whilst this is concise and functionally correct, it doesn't perform well. Keep it simple and use #quamrana's first suggestion - it's much faster
You can sort the list and get the first element, check if it's a negative. With this approach you don't have to iterate over the array:
sorted(series)[0] < 0
There are several improvements you can make:
def has_negative(series):
for i in series:
if i < 0:
return True
return False
or it can be contracted into one line like this:
print(bool([i for i in z if i<0]))
To add:
To keep it clean and short, you could also use a list comprehension within a lambda function as follows:
has_negative = lambda series: True if [series for x in series if x < 0] else False
z = [1,-2,3,4,5,6,7,8,9]
has_negative(z)
Output:
>>> True
In the following code, I use abs(v - i) three times on the same line. Is this expression computed three times when the code is run? Is there a way to avoid this without having to complicate the code?
x = sum(abs(v-i) if s == 1 else int((abs(v-i)*(abs(v-i)+1))/2) for v in list)
Is this expression computed three times when the code is run?
No, once or twice for every list value.
Is there a way to avoid this without having to complicate the code?
Depends on what you consider complicating the code.
You could use the idiom that even got optimized in Python 3.9:
x = sum(a if s == 1 else int((a*(a+1))/2)
for v in list_
for a in [abs(v-i)])
Or if your list values are ints, you could use math.comb:
x = sum(abs(v-i) if s == 1 else comb(abs(v-i)+1, 2) for v in list_)
While https://stackoverflow.com/a/70268402/1126841 regarding the assignment operator is correct, this is a case where I really dislike the assignment expression, as you have to hunt for where a is actually defined. I would probably ditch sum and accumulate the value in a for loop instead.
x = 0
for v in list_:
a = abs(v-i)
if s == 1:
x += a
else:
x += int(a*(a+1)/2)
However, since s never changes in the loop, I would refactor this into two separate loops chosen by the value of s, one of which can use sum without difficulty.
if s == 1:
x = sum(abs(v-i) for v in list_)
else:
x = 0
for v in list_:
a = abs(v-i)
x += int(a*(a+1)/2)
My answer only shows that it's possible to do what you want with a one-liner, but I would still advise to use a longer approach with an explicit if/else + caching the value, or using numpy arrays and masks.
You can use the walrus operator := to store the value as a variable. This line is equivalent to your original code and will only compute a = abs(v-i) once per loop instead of 1-2 times:
x = sum(a if ((a := abs(v-i)) is not None) and s == 1 else int(a*(a+1)/2) for v in list_)
The problem is that the walrus operator can only be used in a if check, so we need to add a check that's always true... It really doesn't help reading comprehension.
"Long" approach:
v = np.array(list_)
a = np.abs(v - i)
x = np.sum(a if s == 1 else np.int(a*(a+1)/2))```
Can't you just save abs(v-i) to a variable and then substitute in that variable?
I would create a variable called my_calc = abs(v-i) then use that name. This will clean up your code
I very new with python and I'm doing an online class to learn the basics. While everything is going well, there are still a things that I don't seem to grasp in python..
Even though I found a much simpler way to solve the bellow problem, I'd still like to understand why my procedure is returning "None" .. Is it mistake with my if statements ? a syntax error ?
Here is the problem:
Define a procedure, union, that takes as inputs two lists.
It should modify the first input list to be the set union of the two lists. You may assume the first list is a set, that is, it contains no repeated elements.
The result we are expecting:
a = [1,2,3]
b = [2,4,6]
union(a,b)
print a
#>>> [1,2,3,4,6]
You will note that, in my procedure, I'm using another procedure to find if a list item is in the other list. Could the problem be coming from that ?
Here is my code:
def find_element(a,b):
if b in a:
return a.index(b)
return - 1
def union(a,b):
i = 0
while i < len(b) - 1:
c = find_element(a,b[i])
if c != -1:
i = i + 1
if c == -1:
a = a.append(b[i])
i = i + 1
return a
a = [1,2,3]
b = [2,4,6]
print(union(a,b))
a = a.append(b[i])
Here, a.append(b[i]) appends b[i] to a and returns 'none' which you have assigned to 'a'.
change this to
a.append(b[i])
and you should atleast get an output.
just in case you may need it.
you code could will be easier to read if you have it like this.
but its nice to challenge yourself.
best,
def union(a, b):
for item in b:
if item not in a:
a.append(item)
return a
a = [1, 2, 3]
b = [2, 4, 6]
x = union(a, b)
print(x)
I wrote a code in Python that print a Fibonacci sequence truncated at a given threshold.
m_char=input('threshold: ')
m=int(m_char)
def fibonacci(m):
lst=[0, 1]
while lst[-1] <= m:
a = lst[-2]+lst[-1]
if a <= m:
lst.append(a)
else:
print(lst)
return
fibonacci(m)
I don't like the double check on the variable m in the while and if statement: I'm pretty sure it is redundant, so there is a way to write more efficient code. I would like to preserve the use of lists. Have you got any idea?
def fibonacci(m):
lst=[0, 1]
a = lst[-2]+lst[-1]
while a <= m:
lst.append(a)
a = lst[-2]+lst[-1]
return lst
You can calculate a once per loop, and use it to determine the whether the loop continues
Just use
while True:
it is the check inside the loop which actually determines how often the loop is run.
It would be slightly more efficient to not use list indexing at all but instead maintain the last two Fibonacci numbers with two variables. Furthermore, it is more idiomatic to return the list rather than print it. Let the calling code print the list if it wants:
def fibonacci(m):
lst=[0, 1]
a,b = lst
while True:
a,b = b, a+b
if b <= m:
lst.append(b)
else:
return lst
I am trying to sort a list, move all 0 to the end of list.
example: [0,1,0,2,3,0,4]->[1,2,3,4,0,0,0]
and I see someone code it in 1 line
list.sort(cmp=lambda a,b:-1 if b==0 else 0)
But I don't understand what inside the parentheses mean.
Could anyone tell me? Thank you.
Preface:
Sort a list according to the normal comparison:
some_list.sort()
Supply a custom comparator:
some_list.sort(cmp=my_comparator)
A lambda function:
x = lambda a, b: a - b
# is roughly the same as
def x(a, b):
return a - b
An if-else-expression:
value = truthy_case if condition else otherwise
# is roughly the same as
if condition:
value = truthy_case
else:
value = otherwise
The line list.sort(cmp=lambda a,b:-1 if b==0 else 0) itself:
Now, the condition in the comparator is whether b==0, if so indicate that b has a bigger value than a (the sign of the result is negative), otherwise indicate that the values compare the same (the sign is zero).
Whilst Python's list.sort() is stable, this code is not sane, because the comparator needs to test a, too, not only b. A proper implementation would use the key argument:
some_list.sort(key=lambda a: 0 if a == 0 else -1)
Fixed list.sort(cmp=...) implementation:
If you want to use list.sort(cmp=...) (you don't) or if you are just curious, this is a sane implementation:
some_list.sort(cmp=lambda a, b: 0 if a == b else
+1 if a == 0 else
-1 if b == 0 else 0)
But notice:
In Py3.0, the cmp parameter was removed entirely (as part of a larger effort to simplify and unify the language, eliminating the conflict between rich comparisons and the __cmp__ methods).
An alternative:
Sorting a list is in O(𝘯 log 𝘯). I do not know if for this simple problem the code runs faster, but I wouldn't think so. An O(𝘯) solution is filtering:
new_list = [x for x in some_list if x != 0]
new_list.extend([0] * (len(some_list) - len(new_list)))
The difference will probably only matter for quite long lists, though.
>>> sorted(l, key=lambda x:str(x) if x == 0 else x)
[1, 3, 4, 8, 0, 0, 0]
Guess what's happening here? I am exploiting the fact that, as a preference, python will pick up integers first, then strings. SO I converted 0 into '0'.
Here's the proof.
>>> ll = [3,2,3, '1', '3', '0']
>>> sorted(ll)
[2, 3, 3, '0', '1', '3']
You should answer yourself and this is plan:
The ternary expression description is available here:
https://docs.python.org/3/reference/expressions.html?highlight=ternary%20operator#conditional-expressions
You can find a lot of expression description in that document:
https://docs.python.org/3/reference/expressions.html
Q: What does lambda mean?
Please spend just 5 days and read a Tutorial about Python language, which is a fork of the original Gvinno Van Rossum book.
https://docs.python.org/3/tutorial/controlflow.html#lambda-expressions