Why does Python 3 print statement appear to alter a variable, declared later in the code, but works fine without it? - python-3.x

I am running Python 3.6.2 on Windows 10 and was learning about the zip() function.
I wanted to print part of the object returned by the zip() function.
Here is my code, without the troublesome print statement:
a = ("John", "Charles", "Mike")
b = ("Jenny", "Christy", "Monica", "Vicky")
x = zip(a, b)
tup = tuple(x)
print(tup)
print(type(tup))
print(len(tup))
print(tup[1])
Here is my code with the troublesome print statement:
a = ("John", "Charles", "Mike")
b = ("Jenny", "Christy", "Monica", "Vicky")
x = zip(a, b)
print(tuple(x)[1])
tup = tuple(x)
print(tup)
print(type(tup))
print(len(tup))
print(tup[1])
The print(tuple(x)[1]) statement appears to change the tuple 'tup' into a zero-length one and causes the print(tup[1]) to fail later in the code!

In this line, you create an iterator:
x = zip(a, b)
Within the print statement, you convert the iterator to a tuple. This tuple has 3 elements. This exhausts the iterator and anytime you call it afterwards, it will return no further elements.
Therefore, upon your creation of tup, your iterator does not return an element. Hence, you have a tuple with length 0. And of course, this will raise an exception when you try to access the element with index 1.
For testing, consider this:
a = ("John", "Charles", "Mike")
b = ("Jenny", "Christy", "Monica", "Vicky")
x = zip(a, b)
tup1 = tuple(x)
tup2 = tuple(x)
print(tup1)
print(tup2)
It will give you the following result:
(('John', 'Jenny'), ('Charles', 'Christy'), ('Mike', 'Monica'))
()
This is basically what you do when creating a tuple out of an iterator twice.

Related

list comprehension for empty list python 3?

I have an python 3 code as follow:
a = []
b = [[0] * len(a[0]) for _ in range(len(a))]
The above code works fine, but the follow code does not work:
a = []
m, n = len(a), len(a[0])
len(a[0]) apppears in both codes, why the list comprehension does not through IndexError: list index out of range.
Thanks,
range(len(a)) in this case is essentially range(0), which is an empty range:
>>> list(range(0))
[]
Because the collection being iterated over is empty, the comprehension never runs, so a[0] is never evaluated.
It's similar to how this loop prints nothing:
for _ in []:
print("SOME TEXT!")
[] is empty, so the for loop never iterates.
With m, n = len(a), len(a[0]) however, a[0] is run regardless of the length of a, so a[0] is evaluated, and you get an error.

Python assigning variables with an OR on assignment, multiple statements in one line?

I am not super familiar with python, and I am having trouble reading this code. I have never seen this syntax, where there multiple statements are paired together (I think) on one line, separated by commas.
if L1.data < L2.data:
tail.next, L1 = L1, L1.next
Also, I don't understand assignment in python with "or": where is the conditional getting evaluated? See this example. When would tail.next be assigned L1, and when would tail.next be assigned L2?
tail.next = L1 or L2
Any clarification would be greatly appreciated. I haven't been able to find much on either syntax
See below
>>> a = 0
>>> b = 1
>>> a, b
(0, 1)
>>> a, b = b, a
>>> a, b
(1, 0)
>>>
It allows one to swap values without requiring a temporary variable.
In your case, the line
tail.next, L1 = L1, L1.next
is equivalent to
tail.next = L1
L1 = L1.next
In python when we write any comma separated values it creates a tuple (a kind of a datastructure).
a = 4,5
type(a) --> tuple
This is called tuple packing.
When we do:
a, b = 4,5
This is called tuple unpacking. It is equivalent to:
a = 4
b = 5
or is the boolean operator here.

iteration and matching items in lists

Am trying to check if elements of a list match elements of another. But there is a slight twist to the problem.
alist = ['949', '714']
blist = ['(714)824-1234', '(419)312-8732', '(949)555-1234', '(661)949-2867']
Am trying to match the elements of alist to the blist, but only the area code part(in blist). Here is my current code:
def match_area_codes(alist, blist):
clist =[]
for i in alist:
for j in blist:
if i in j:
clist.append(j)
return clist
The code works for the most part, except when there is a string matching the area code anywhere else in the list. It should only print:
['(714)824-1234', '(949)555-1234']
but it ends up printing
['(714)824-1234', '(949)555-1234', '(661)949-2867']
as there is a '949' in the last phone number. Is there a way to fix this?
You can use a regular expression to get the part within (...) and compare that part to alist.
import re
def match_area_codes(alist, blist):
p = re.compile(r"\((\d+)\)")
return [b for b in blist if p.search(b).group(1) in alist]
Example:
>>> alist = set(['949', '714'])
>>> blist = ['(714)824-1234', '(419)312-8732', '(949)555-1234', '(661)949-2867']
>>> match_area_codes(alist, blist)
['(714)824-1234', '(949)555-1234']
If you really really want to do it without regular expressions, you could, e.g., find the position of the ( and ) and thus get the slice from the string corresponding to the region code.
def match_area_codes(alist, blist):
find_code = lambda s: s[s.index("(") + 1 : s.index(")")]
return [b for b in blist if find_code(b) in alist]
However, I would strongly suggest to just take this as an opportunity for getting started with regular expressions. It's not all that hard, and definitely worth it!

Scala Comprehension Errors

I am working on some of the exercism.io exercises. The current one I am working on is for Scala DNA exercise. Here is my code and the errors that I am receiving:
For reference, DNA is instantiated with a strand String. This DNA can call count (which counts the strand for the single nucleotide passed) and nucletideCounts which counts all of the respective occurrences of each nucleotide in the strand and returns a Map[Char,Int].
class DNA(strand:String) {
def count(nucleotide:Char): Int = {
strand.count(_ == nucleotide)
}
def nucleotideCounts = (
for {
n <- strand
c <- count(n)
} yield (n, c)
).toMap
}
The errors I am receiving are:
Error:(10, 17) value map is not a member of Int
c <- count(n)
^
Error:(12, 5) Cannot prove that Char <:< (T, U). ).toMap
^
Error:(12, 5) not enough arguments for method toMap: (implicit ev:
<:<[Char,(T, U)])scala.collection.immutable.Map[T,U]. Unspecified
value parameter ev. ).toMap
^
I am quite new to Scala, so any enlightenment on why these errors are occurring and suggestions to fixing them would be greatly appreciated.
for comprehensions work over Traversable's that have flatMap and map methods defined, as the error message is pointing out.
In your case count returns with a simple integer so no need to "iterate" over it, just simply add it to your result set.
for {
n <- strand
} yield (n, count(n))
On a side note this solution is not too optimal as in the case of a strand AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA count is going to be called many times. I would recommend calling toSet so you get the distinct Chars only:
for {
n <- strand.toSet
} yield (n, count(n))
In line with Akos's approach, consider a parallel traversal of a given strand (String),
strand.distinct.par.map( n => n -> count(n) )
Here we use distinct to gather unique items and construct each Map association in map.
A pipeline solution would look like:
def nucleotideCounts() = strand.groupBy(identity).mapValues(_.length)
Another approach is
Map() ++ {for (n <- strand; c = count(n)) yield n->c}
Not sure why it's different than {...}.toMap() but it gets the job done!
Another way to go is
Map() ++ {for (n <- strand; c <- Seq(count(n))) yield n->c}

Using string as variable in iPython interactive

I would like to run the following in iPython:
mylist = ['a','b']
def f(a,b):
do_something
sliderinterval=(0,10,1)
w = interactive(f, a = sliderinterval, b = sliderinterval)
but instead of writing a and b, I would like to take them from mylist. Is that possible?
Make a dict comprehension, and then pass the dictionary to the function by unpacking (**) in as keywords arguments.
mylist = ['a','b']
def f(a,b):
print(a,b)
sliderinterval=(0,10,1)
d = {k:sliderinterval for k in mylist}
w = interactive(f, **d)
**d is equivalent to writing manually key1=value1, key2=value2... you will often see it in function signature as **kwargs or **kw, for unpacking list you will need only one star and see to as *args.

Resources