Related
I am trying to write python code to find and replace value in a list.
Steps:
I have one element in a list [8]
I need to find the current element (8 in our case) reduce with value of 2, then add (Prefix and Suffix) as separate element in the same list ['6','8','6']
Taking the first element from the latest list ('6'), repeat Step 1 and Step 2 ('4','6','4'), and replace with all the repeating/duplicate/reoccurring first element('6') of the list.['4','6','4','8','4','6','4']
Repeat the steps above until and unless we have number>0 as first element.['2','4','2','6','2','4','2','8','2','4','2','6','2','4','2']
another example:
[10]
[8,10,8]
[6,8,6,10,6,8,6]
[4,6,4,8,4,6,4,10,4,6,4,8,4,6,4]
[2,4,2,6,2,4,2,8,2,4,2,6,2,4,2,10,2,4,2,6,2,4,2,8,2,4,2,6,2,4,2]
I was able to do for string_length<10
string = '8'
i = int(string)
while i>2:
print(string)
temp = str(int(string[0])-2) + string[0] + str(int(string[0])-2)
string = string.replace(string[0],temp)
i -= 2
print(string)
output:
8
686
4648464
242624282426242
for single, digit I can do it but I am facing problem for values more than 9.
can some one help me in programming this?
Is there any other way to do the same with out list?
Firstly, your examples are inconsistent between strings, lists of strings, and lists of ints, but the simplest option here is a list of ints, so that's what I'll use. You can always convert later if needed.
This is a great situation for recursion. You don't get the intermediate results like in the question, but the process is much more streamlined.
def expand(number, k=2):
if number == k:
return [number]
expansion = expand(number-k, k)
return expansion + [number] + expansion
print(expand(8)) # -> [2, 4, 2, 6, 2, 4, 2, 8, 2, 4, 2, 6, 2, 4, 2]
This code should be self-documenting, but let me know if anything's unclear.
This question already has answers here:
How does assignment work with list slices?
(5 answers)
Closed 5 years ago.
I came across the following code (sort of):
my_list = [1, [2, 3, 4], 5]
my_list[1:2] = my_list[1]
After running these two lines, the variable my_list will be [1, 2, 3, 4, 5]. Pretty useful for expanding nested lists.
But why does it actually do what it does?
I would have assumed that the statement my_list[1:2] = my_list[1] would do one of the following:
simply put [2, 3, 4] into the second position in the list (where it already is)
give some kind of "too many values to unpack" error, from trying to put three values (namely 2,3,4) into a container of only length 1 (namely my_list[1:2]). (Repeating the above with a Numpy array instead of a list results in a similar error.)
Other questions (e.g. How assignment works with python list slice) tend to not pay much attention to the discrepancy between the size of the slice to be replaced, and the size of the items you're replacing it with. (Let alone explaining why it works the way it does.)
Slice assignment replaces the specified part of the list with the iterable on the right-hand side, which may have a different length than the slice. Taking the question at face value, the reason why this is so is because it's convenient.
You are not really assigning to the slice, i.e. Python doesn't produce a slice object that contains the specified values from the list and then changes these values. One reason that wouldn't work is that slicing returns a new list, so this operation wouldn't change the original list.
Also see this question, which emphasizes that slicing and slice assignment are totally different.
Here is the relevant bit from the Python Language Reference
If the target is a slicing: The primary expression in the reference is
evaluated. It should yield a mutable sequence object (such as a list).
The assigned object should be a sequence object of the same type.
Next, the lower and upper bound expressions are evaluated, insofar
they are present; defaults are zero and the sequence’s length. The
bounds should evaluate to integers. If either bound is negative, the
sequence’s length is added to it. The resulting bounds are clipped to
lie between zero and the sequence’s length, inclusive. Finally, the
sequence object is asked to replace the slice with the items of the
assigned sequence. The length of the slice may be different from the
length of the assigned sequence, thus changing the length of the
target sequence, if the target sequence allows it.
This behavior makes sense qualitatively because when you slice a list you get a sub list so replacing that with another list shouldn't add a level of nesting. Allowing it to change the length of the list is a design choice. Other choices are possible as your numpy example demonstrates.
Short Answer:
my_list[1:2] = my_list[1] will replaced the content from 1st index to 2nd index of my_list with the content of present in 1st index of
my_list
Explanation:
Let's see two slicing operations, very similar but totally distinct
This creates the copy of list and stores it the variable
some_variable = some_list[2:5]
This replaces the content of the list inplace, which permits changing the length of the list too.
some_list[2:5] = [1, 2, 3, 4]
When you use assignment operator =, it invokes a __setitem__ function. Our focus here is the case 2 above. As per the Python's Assignment Statement document:
If the target is a slicing: The primary expression in the reference is
evaluated. It should yield a mutable sequence object (such as a list).
The assigned object should be a sequence object of the same type.
Next, the lower and upper bound expressions are evaluated, insofar
they are present; defaults are zero and the sequence’s length. The
bounds should evaluate to integers. If either bound is negative, the
sequence’s length is added to it. The resulting bounds are clipped to
lie between zero and the sequence’s length, inclusive. Finally, the
sequence object is asked to replace the slice with the items of the
assigned sequence. The length of the slice may be different from the
length of the assigned sequence, thus changing the length of the
target sequence, if the target sequence allows it.
In our case my_list[1:2] = my_list[1], python will also call __setitem__ as:
my_list.__setitem__(slice(1,2,None), [2, 3, 4])
Refer slice document to know what it does.
So, when you did my_list[1:2] = my_list[1], you replaced the content from 1st index to 2nd index of my_list with the content of present in 1st index of my_list i.e. [2, 3, 4].
I think now we can answer why your assumptions are incorrect:
put [2, 3, 4] into the second position in the list (where it already is)
No. Because __setitem__ is not called on the index but on the slice of the indices which you passed.
give some kind of "too many values to unpack" error, from trying to put three values (namely 2,3,4) into a container of only length 1 (namely my_list[1:2]).
Again No. Because the range of your indices creating your container is replaced with new set of values.
What you are doing is slice assignment.
Assignment to slices is also possible, and this can even change the size of the list or clear it entirely
my_list[1:2] = my_list[1]
This replaces the slice of my_list with the contents of my_list[1].
By specifying my_list[1:2] on the left side of the assignment operator =, you are telling Python you want to use slice assignment.
my_list[1:2] = my_list[1] is equivalent to my_list.__setitem__(slice(1, 2, None), my_list[1])
In slice(1, 2, None), 1 is start, 2 is stop, and None is step and is optional.
What you are trying here is called Slice Assingnment. In python it is possible to assign an iterable(my_list[1] in your case) to a slice of another iterable(my_list[0:1] in your case). Lets walk through some examples to understand what it really means:
>>> l = [1,2,3,4,5]
>>> b = [6,7,8]
>>> l[0:3] = b
>>> l
>>> [6, 7, 8, 4, 5]
So what happened here is the portion of list l for 0,1,2 indices is
which covers elements 1,2,3is replaced by elements of list b 6,7,8. However in this case size of slice and replaced elements happens to be equal by chance.
So what happens when slice size and iterable to be replaced are not equal
>>> l = [1,2,3,4,5]
>>> b = [6,7,8]
>>> l[0:4] = b
>>> l
>>> [6,7,8,5]
Notice that this operation didn't produce any error, instead, it just copied whatever elements are available with the entire sliced portion. In this case, sliced elements are 1,2,3,4 replaced by 6,7,8
In the previous example iterable to be replaced was smaller. What happens if slice portion is smaller
>>> l = [1,2,3,4,5]
>>> b = [6,7,8]
>>> l[0:1] = b
>>> l
>>> [6,7,8,2,3,4,5]
So now we can see that only first element is replaced by entire iterable b.
You can also use this behaviour to remove a specific portion of the list ( Which I find convenient in some situations ).
>>> l = [1,2,3,4,5]
>>> l[0:2] = []
>>> l
>>> [3,4,5]
First two elements are removed very conveniently here.
So the example in your question is similar to the examples I posted above, except that in your case there is an additional step of unpacking list values. Unpacking list value happens every time when you assign list to another list. A short example
>>> l = [[1]]
>>> a = []
>>> a = l[0]
>>> a
>>> [1]
Your example now:
#Replace the slice [0:1] with my_list[1] perform unpacking list values as well
>>> my_list[1:2] = my_list[1]
>>> [1,2,3,4,5]
Also note that slice assignment is only possible if you assign iterable to a slice. If you try to assign an int or something that is not an iterable to a slice python will throw an error.
>>> l = [1,2,3,4,5]
>>> b = [6,7,8]
>>> l[0:1] = b[1]
>>> Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: can only assign an iterable
That's why in your case my_list[1] don't raise an error since it is an iterable.
That what you are doing is insert an element through slicing. I will explain everything by parts. More than an inserting it could be interpreted as adding an item to your list after slicing target list in a range desired. Now to explain every line in detail:
my_list[1:2]
That part is like saying to Python; "I want to get the values (slice) from index 1 to the index before 2 (excluding 2, I will explain with another example later on)". After that you assign a value to those values:
my_list[1:2] = my_list[1] #The same as my_list[1:2] = [2,3,4]
Now that you know what the first part does, next it is going to add the item at the right side of the '=' operator so you could interpret it like this; "I want to slice from index 1 to everything before 2 (again, excluding index 2) and then add my list [2,3,4]". Now here comes another examples so you understand even better I hope.
problemList = [1, [2, 3, 4], 5]
problemList[1:2] = problemList[1] #The same as problemList[1:2] = [2,3,4]
analogProblemL = [1] + [2,3,4] + [5] #Output : [1,2,3,4,5]
insertList = [12,13,14]
myList = [1, [2, 3, 4], 5,6,7,8,9]
myList[3:6] = insertList
analogFstList = [1,[2,3,4] ,5] + insertList + [9] #Output : [1,[2,3,4],5,12,13,14,9]
myScnList = [1, [2, 3, 4], 5]
myScnList[1:3] = [2,3,4]
analogScnList = [1] + [2,3,4] + [5] #Output : [1,2,3,4,5]
The next lines will be like if it was an animation frames so it's easier to interpret:
[1,2,3,4,5] #List before line of code: myList[1:3] = [12,13,14]
[1,|2,3|,4,5] #The moment where you slice the list with: myList[1:3]. Please notice that the characters used '|' are for representing the slice.
[1] + [12,13,14] + [4,5] #After assigning what the slice should be changed for. It's like excracting from the whole list the values [2,3] and changing it for [12,13,14].
[1,12,13,14,4,5] #Final list after running the code.
Some references used for this answer:
http://effbot.org/zone/python-list.htm
Understanding Python's slice notation
How assignment works with python list slice
Hope it was useful for you.
Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 7 years ago.
Improve this question
I'm learning Python, coming from a C#/Java background, and was playing around with list behavior. I've read some of the documentation, but I don't understand how, or why, slices with indices larger than length-1 make is possible to append items.
ls = ["a", "b", "c", "d"]
n = len(ls) # n = 4
letter = ls[4] # index out of range (list indexes are 0 - 3)
# the following all do the same thing
ls += ["echo"]
ls.append("foxtrot")
ls[len(ls):] = ["golf"] # equivalent to ls[len(ls): len(ls)]
print(ls)
Although it seems odd to me, I understand how slices can modify the list they operate on. What I don't get is why list[len(list)] results in the expected out of bounds error, but list[len(list):] doesn't. I understand that slices are fundamentally different than indexes, just not what happens internally when a slice begins with an index outside of the list values.
Why can I return a slice from a list that starts with a non-existent element (len(list))? And why does this allow me to expand the list?
Also, of the first three above methods for appending an item, which one is preferred from a convention or performance perspective? Are there performance disadvantages to any of them?
While a.append(x) and a[len(a):] = [x] do the same thing, the first is clearer and should almost always be used. Slice replacement may have a place, but you shouldn't look for reasons to do it. If the situation warrants it, it should be obvious when to do it.
Start with the fact that indexing returns an element of the list, and slicing returns a "sublist" of a list. While an element at a particular either exists or does not exist, it is mathematically convenient to think of every list have the empty list as a "sublist" between any two adjacent positions. Consider
>>> a = [1, 2, 3]
>>> a[2:2]
[]
Now consider the following sequence. At each step, we increment the starting index of the slice
>>> a[0:] # A copy of the list
[1, 2, 3]
>>> a[1:]
[2, 3]
>>> a[2:]
[3]
Each time we do so, we remove one more element from the beginning of the list for the resulting slice. So what should happen when the starting index finally equals the final index (which is implicitly the length of the list)?
>>> a[3:]
[]
You get the empty list, which is consistent with the following identities
a + [] = a
a[:k] + a[k:] = a
(The first is a special case of the second where k >= len(a).
Now, once we see why it makes sense for the slice to exist, what does it mean to replace that slice with another list? In other words, what list do we get if we replace the empty list at the end of [1, 2, 3] with the list [4]? You get [1, 2, 3, 4], which is consistent with
[1, 2, 3] + [4] = [1, 2, 3, 4]
Consider another sequence, this time involving slice replacements.
>>> a = [1,2,3]; a[0:] = ['a']; a
['a']
>>> a = [1,2,3]; a[1:] = ['a']; a
[1, 'a']
>>> a = [1,2,3]; a[2:] = ['a']; a
[1, 2, 'a']
>>> a = [1,2,3]; a[3:] = ['a']; a
[1, 2, 3, 'a']
Another way of looking at slice replacement is to treat it as appending a list to a sublist of the original. When that sublist is the list itself, then it is equal to appending to the original.
Why can I return a slice from a list that starts with a non-existent element?
I can't tell for sure, but my guess would be that it's because a reasonable value can be returned (there are no elements this refers to, so return an empty list).
And why does this allow me to expand the list?
Your slice selects the last 0 elements of the list. You then replace those elements with the elements of another list.
Also, of the first three above methods for appending an item, which one is preferred
Use .append() if you're adding a single element, and .extend() if you have elements inside another list.
I'm not sure about performance, but my guess would be that those are pretty well optimized.
I would like to ask what the following does in Python.
It was taken from http://danieljlewis.org/files/2010/06/Jenks.pdf
I have entered comments telling what I think is happening there.
# Seems to be a function that returns a float vector
# dataList seems to be a vector of flat.
# numClass seems to an int
def getJenksBreaks( dataList, numClass ):
# dataList seems to be a vector of float. "Sort" seems to sort it ascendingly
dataList.sort()
# create a 1-dimensional vector
mat1 = []
# "in range" seems to be something like "for i = 0 to len(dataList)+1)
for i in range(0,len(dataList)+1):
# create a 1-dimensional-vector?
temp = []
for j in range(0,numClass+1):
# append a zero to the vector?
temp.append(0)
# append the vector to a vector??
mat1.append(temp)
(...)
I am a little confused because in the pdf there are no explicit variable declarations. However I think and hope I could guess the variables.
Yes, the method append() adds elements to the end of the list. I think your interpretation of the code is correct.
But note the following:
x =[1,2,3,4]
x.append(5)
print(x)
[1, 2, 3, 4, 5]
while
x.append([6,7])
print(x)
[1, 2, 3, 4, 5, [6, 7]]
If you want something like
[1, 2, 3, 4, 5, 6, 7]
you may use extend()
x.extend([6,7])
print(x)
[1, 2, 3, 4, 5, 6, 7]
Python doesn't have explicit variable declarations. It's dynamically typed, variables are whatever type they get assigned to.
Your assessment of the code is pretty much correct.
One detail: The range function goes up to, but does not include, the last element. So the +1 in the second argument to range causes the last iterated value to be len(dataList) and numClass, respectively. This looks suspicious, because the range is zero-indexed, which means it will perform a total of len(dataList) + 1 iterations (which seems suspicious).
Presumably dataList.sort() modifies the original value of dataList, which is the traditional behavior of the .sort() method.
It is indeed appending the new vector to the initial one, if you look at the full source code there are several blocks that continue to concatenate more vectors to mat1.
append is a list function used to append a value at the end of the list
mat1 and temp together are creating a 2D array (eg = [[], [], []]) or matrix of (m x n)
where m = len(dataList)+1 and n = numClass
the resultant matrix is a zero martix as all its value is 0.
In Python, variables are implicitely declared. When you type this:
i = 1
i is set to a value of 1, which happens to be an integer. So we will talk of i as being an integer, although i is only a reference to an integer value. The consequence of that is that you don't need type declarations as in C++ or Java.
Your understanding is mostly correct, as for the comments. [] refers to a list. You can think of it as a linked-list (although its actual implementation is closer to std::vectors for instance).
As Python variables are only references to objects in general, lists are effectively lists of references, and can potentially hold any kind of values. This is valid Python:
# A vector of numbers
vect = [1.0, 2.0, 3.0, 4.0]
But this is perfectly valid code as well:
# The list of my objects:
list = [1, [2,"a"], True, 'foo', object()]
This list contains an integer, another list, a boolean... In Python, you usually rely on duck typing for your variable types, so this is not a problem.
Finally, one of the methods of list is sort, which sorts it in-place, as you correctly guessed, and the range function generates a range of numbers.
The syntax for x in L: ... iterates over the content of L (assuming it is iterable) and sets the variable x to each of the successive values in that context. For example:
>>> for x in ['a', 'b', 'c']:
... print x
a
b
c
Since range generates a range of numbers, this is effectively the idiomatic way to generate a for i = 0; i < N; i += 1 type of loop:
>>> for i in range(4): # range(4) == [0,1,2,3]
... print i
0
1
2
3
I just started learning recursion and I have an assignment to write a program that tells the nesting depth of a list. Well, I browsed around and found working code to do this, but I'm still having trouble understanding how it works. Here's the code:
def depth(L) :
nesting = []
for c in L:
if type(c) == type(nesting) :
nesting.append(depth(c))
if len(nesting) > 0:
return 1 + max(nesting)
return 1
So naturally, I start to get confused at the line with the append that calls recursion. Does anyone have a simple way of explaining what's going on here? I'm not sure what is actually being appended, and going through it with test cases in my head isn't helping. Thanks!
edit: sorry if the formatting is poor, I typed this from my phone
Let me show it to you the easy way, change the code like this:
(### are the new lines I added to your code so you can watch what is happening there)
def depth(L) :
nesting = []
for c in L:
if type(c) == type(nesting) :
print 'nesting before append', nesting ###
nesting.append(depth(c))
print 'nesting after append', nesting ###
if len(nesting) > 0:
return 1 + max(nesting)
return 1
Now lets make a list with the depth of three:
l=[[1,2,3],[1,2,[4]],'asdfg']
You can see our list has 3 element. one of them is a list, the other is a list which has another list in itself and the last one is a string. You can clearly see the depth of this list is 3 (i.e there are 2 lists nested together in the second element of the main list)
Lets run this code:
>>> depth(l)
nesting before append []
nesting after append [1]
nesting before append [1]
nesting before append []
nesting after append [1]
nesting after append [1, 2]
3
Piece of cake! this function appends 1 to the nesting. then if the element has also another list it appends 1 + maximum number in nesting which is the number of time function has been called itself. and if the element is a string, it skips it.
At the end, it returns the maximum number in the nesting which is the maximum number of times recursion happened, which is the number of time there is a list inside list in the main list, aka depth. In our case recursion happened twice for the second element + 1=3 as we expected.
If you still have problem getting it, try to add more print statements or other variables to the function and watch them carefully and eventually you'll get it.
So what this seems to be is a function that takes a list and calculates, as you put it, the nesting depth of it. nesting is a list, so what if type(c) == type(nesting) is saying is: if the item in list L is a list, run the function again and append it and when it runs the function again, it will do the same test until there are no more nested lists in list L and then return 1 + the max amount of nested lists because every list has a depth of 1.
Please tell me if any of this is unclear
Let's start with a couple of examples.
First, let's consider a list with only one level of depth. For Example, [1, 2, 3].
In the above list, the code starts with a call to depth() with L = [1, 2, 3]. It makes an empty list nesting. Iterates over all the elements of L i.e 1, 2, 3 and does not find a single element which passes the test type(c) == type(nesting). The check that len(nesting) > 0 fails and the code returns a 1, which is the depth of the list.
Next, let's take an example with a depth of 2, i.e [[1, 2], 3]. The function depth() is called with L = [[1, 2], 3] and an empty list nesting is created. The loop iterates over the 2 elements of L i.e [1, 2] , 3 and since type([1, 2]) == type(nesting), nesting.append(depth(c)) is called. Similar to the previous example, depth(c) i.e depth([1, 2]) returns a 1 and nesting now becomes [1]. After the execution of the loop, the code evaluates the test len(nesting) > 0 which results in True and 1 + max(nesting) which is 1 + 1 = 2 is returned.
Similarly, the code follows for the depth 3 and so on.
Hope this was helpful.
This algorithm visits the nested lists and adds one for each level of recursion. The call chain is like this:
depth([1, 2, [3, [4, 5], 6], 7]) =
1 + depth([3, [4, 5], 6]) = 3
1 + depth([4, 5]) = 2
1
Since depth([4,5]) never enters the if type(c) == type(nesting) condition because no element is a list, it returns 1 from the outer return, which is the base case.
In the case where, for a given depth, you have more than one nested list, e.g. [1, [2, 3], [4, [5, 6]], both the max depth of [2,3]and [4, [5, 6]] are appended on a depth call, of which the max is returned by the inside return.