I have trying to achieve the following. I wrote code to or the values of a string and count the number of 1's in the string.
a='11001'
b='00111'
a and b
'00111'
a or b
11001
Can someone explain how this works. The answer that I am expecting is that when I do a and b, the result should be 00001 and when I do a or b, I should get 11111, the logical and and or.
I do understand that non empty strings yield True for all cases. So I can explain the above behavior, thought I do not understand how to implement boolean and and or
Thanks in advance.
a='11001'
b='00111'
c = ''
case "and":
for bit_1, bit_2 in zip(a, b):
if bit_1 == bit_2:
c += '1'
else:
c += '0'
case "or":
for bit_1, bit_2 in zip(a, b):
if bit_1 == '1' or bit_2 == '1':
c += '1'
else:
c += '0'
The and keyword does not work like you expect. What it does is:
evaluate the left-hand operand.
if that operand is falsy (None, False, 0, empty string, …) then return it.
otherwise evaluate the right-hand operand and return it.
There, '11001' is not falsy, as it's a non-empty string so '11101' and '00111' returns the second string.
Maybe you wanted to do a bitwise operation, using the bitwise and operator, &?
>> 0b11001 & 0b00111
1
and and or keywords work on Boolean values. A non-empty string is True in python.
If you want to perform bit operations, you can use something like this:
>>> a='11001'
>>> b='00111'
>>> bin(int(a, 2) & int(b, 2))
'0b1'
>>> bin(int(a, 2) | int(b, 2))
'0b11111'
And to count the number of 1's in the or result, you can use the count() function:
>>> bin(int(a, 2) | int(b, 2))[2:]
'11111'
>>> bin(int(a, 2) | int(b, 2))[2:].count('1')
5
Related
Why does this work:
def hamming_distance(dna_1,dna_2):
hamming_distance = sum(1 for a, b in zip(dna_1, dna_2) if a != b)
return hamming_distance
As opposed to this:
def hamming_distance(dna_1,dna_2):
hamming_distance = sum(for a, b in zip(dna_1, dna_2) if a != b)
return hamming_distance
I get this error:
Input In [90]
hamming_distance = sum(for a, b in zip(dna_1, dna_2) if a != b)
^
SyntaxError: invalid syntax
I expected the function to work without the 1 after the ()
The working expression can be unrolled into something like this:
hamming_distance = 0
for a, b in zip(dna_1, dna_2):
if a != b:
hamming_distance += 1
Without a number after +=, what should Python add? It doesn't know, and neither do we.
If this "unrolled" syntax or your code's relationship to it is new to you, probably start by reading up on list comprehensions, which generalize into generator expressions (which is what you have).
You wrote a generator expression. Generator expressions must produce a value (some expression to the left of the first for). Without it, you're saying "please sum all the lack-of-values not-produced by this generator expression".
Ask yourself:
What does a genexpr that produces nothing even mean?
What is sum summing when it's being passed a series of absolute nothing?
You could write a shorter genexpr with the same effect with:
hamming_distance = sum(a != b for a, b in zip(dna_1, dna_2))
since bools have integer values of 1 (for True) and 0 (for False), so it would still work, but it would be slower than sum(1 for a, b in zip(dna_1, dna_2) if a != b) (which produces fewer values for sum to work on and, at least on some versions of Python, allows sum to operate faster, since it has a fast path for summing small exact int types that bool breaks).
As far as I know, the in operator in Python can't be chained or at least I couldn't find any info on it, here is my problem
Here is the code
arr = [1, True, 'a', 2]
print('a' in arr in arr) # prints False
print(('a' in arr) in arr) # prints True
What I don't understand is the first print, I know in the second the first in returns True and then it check if True is in arr, but what about the first one? Does it check if 'a' is in arr and then if arr is in arr?
The premise is false; the in operator can be chained. See Comparisons in the docs:
comp_operator ::= "<" | ">" | "==" | ">=" | "<=" | "!="
| "is" ["not"] | ["not"] "in"
So, just as with any other chained comparison, a in b in c is equivalent to (a in b) and (b in c) (except that b is only evaluated once.
The reason 'a' in arr in arr is false is that arr in arr is false. The only time x in x is true is if x is type that does substring comparisons for __contains__ (like str or bytes), or if it's a container that actually contains itself (like lst = []; lst.append(lst)).
list1 = ['Hello', 10, None]
list2 = [g.lower() for g in list1 if isinstance(g, str)]
list3 = [g.lower() if isinstance(g,str) else g for g in list1]
list4 = [isinstance(g, str) and g.lower() or g for g in list1]
If I want to convert the string in list to lowercase, I can use the method in list2 and the output will be ['hello'].
In addition to this conversion, if I want to keep integers (which is 10 in this case) and None, the methods in both list3 and list4 will work and the output will be ['hello', 10, None].
My question is that I can't understand how the method in list4 works.
To start, writing code like this:
condition and value1 or value2
was how people implemented a ternary conditional operator in Python before the:
value1 if condition else value2
conditional expression was introduced in version 2.5 because of PEP 0308. Using the old method is now deprecated in favor of the slightly more efficient and far more readable newer method.
The old method works because of how and and or operate in Python. Instead of returning boolean results like in most other languages, these operators return values.
Doing a and b returns a if a evaluates to False; otherwise, it returns b:
>>> 0 and 1
0
>>> 1 and 0
0
>>> 1 and 2
2
>>>
Doing a or b returns a if a evaluates to True; otherwise, it returns b:
>>> 1 or 0
1
>>> 0 or 1
1
>>> 1 or 2
1
>>>
Also, in case you do not know, 0 evaluates to False while every other number evaluates to True.
Coming to your code, this:
isinstance(g, str) and g.lower() or g
is actually interpreted by Python like:
(isinstance(g, str) and g.lower()) or g
Now if isinstance(g, str) returns False (g is not a string):
(False and g.lower()) or g
False is returned by and:
False or g
and then or returns g. Thus, we avoided calling .lower() on a non-string type.
If however isinstance(g, str) returns True (g is a string):
(True and g.lower()) or g
and returns g.lower():
g.lower() or g
and then or returns g.lower(), which is fine because g is a string.
Summed up, these two expressions:
g.lower() if isinstance(g,str) else g
isinstance(g, str) and g.lower() or g
are functionally equivalent. But please use the first!! The other is terrible for readability.
Quoting the doc:
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.
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.
Because of precedence rules, isinstance(g, str) and g.lower() or g is actually evaluated as
(isinstance(g, str) and g.lower()) or g (multiplication is of higher precedence than addition).
That basically means the following:
if isinstance(g, str) is true, result of g.lower() will be taken
otherwise g will be taken
As you see, it's the same thing that you have in list3 operation.
I hope I'm missing something obvious here, but I was playing around with the Python CodingBat site, and got to the problem front_back:
For a string passed in, return it with its first and last characters swapped, if the string length is greater than 1.
I came up with a single line solution which I thought would suffice but Coding.bat refuses to accept it, with an Index out of range error.
I've played around in IDLE (64bit windows version) and I've boiled the problem down to this:
len(str) < 2 and str or 'doh ' + str + ' /doh'
The wierd problem is that setting str = '' returns:
'doh /doh'
Which it shouldn't as len('') is 0, but str='a' returns:
'a'
and str='abc' returns:
'doh abc /doh'
Which I would expect...
So my question is really; Why is checking the len of '' going to the OR condition of the ternary operator, but running len('') at the console return 0, which is obviously less than 2?
Edit:
This solution actually works, however:
def front_back(str):
if len(str) < 2:
return str
else:
return str[-1] + str[1:-1] + str[0]
To 'mtadd'; thanks for your answer but this isn't a logical and/or this is a pythonic ternary operator:
return (statement) and <statement was true> or <statement was false>
It's the same as C#'s:
return statement
? trueValue
: falseValue;
Its a matter of operator precedence.
In the case of your expressions, the implicit ordering is as follows
((len(str) < 2) and str) or ('doh ' + str + ' /doh')
In the case of str = "", len(str) < 2 evaluates True, but str, an empty string, evaluates to False, thus the and expression is False, so the right operand of the or binary op becomes the result of the expression.
For the case of str = "abc", len(str) < 2 is False, so the and expression short-circuits as False, and the expression to the right of the or is the result.
For the case of str = "ab", len(str) < 2 is True, so the and expression passes its right operand to the or binary operator, and since bool("ab") == True, the value of str becomes the result of the expression.
As you can see, using and/or will not work similarly to other language's ternary operators if your result in the case of a True condition is equivalent to False, e.g.
True and 0 or 1 yields 1, whereas using 0 if True else 1 yields 0.
I'd recommend using python's ternary if/else expression, e.g.:
str if len(str) < 2 else 'doh ' + str + ' /doh'
def front_times(str, n):
return str[0:3] * n
string='a'
p=0
while (p <len(string)) & (string[p]!='c') :
p +=1
print ('the end but the process already died ')
while (p <1) & (string[p]!='c') :
IndexError: string index out of range
I want to test a condition up to the end of a string (example string length=1)
why are both parts of the and executed is the condition is already false!
as long as p < len(string). the second part does not even need executing.
if it does a lot of performance can be lost
You're not using proper boolean and. Use it and you won't see this problem. What you're using (&) is a bitwise comparison, which evaluates both sides.
Bitwise AND, "a & b", should be thought of as
function _bitwise_and(A,B):
# A and B are Python expressions
# which result in lists of 1's and 0's
a = A.evaluate()
b = B.evaluate()
return [ 1 if abit==1 and bbit==1 else 0 for abit,bbit in zip(a,b)]
so, graphically,
a: ... 0 1 1 0
b: ... 1 0 1 0
--------
a&b ... 0 0 1 0 <- each bit is 1 if-and-only-if the
corresponding input bits are both 1
and the result is a list of bits, packed into an integer.
.
Logical AND, "a and b", should instead be thought of as
function _and(A,B):
# A and B are Python expressions which result in values having truthiness
a = A.evaluate()
if is_truthy(a):
b = B.evaluate()
return b
else:
return a
.
Notice: if the result of A is falsy, B never gets evaluated - so if expression B has an error when evaluated, bitwise AND will result in an error while logical AND will not.
This is the basis for the common Python idiom,
while (offset in data) and test(data[offset]):
do_something_to(data[offset])
next offset
... because data[offset] is only evaluated if offset is a useable (non-error-producing) value.
By using '&' instead of 'and', you guarantee an error by evaluating data[last_offset+1] at the end of your loop.
.
Of course, this could have been avoided with another common idiom:
for ch in string if ch=='c':
do_something_to(ch)
which avoids IndexError problems altogether.
You need to use the boolean operators and and or rather than the bitwise operators & and |