Reversing ONLY integers in list without slice or reverse() - python

I am trying to create a program to reverse only the integers in a given list, ignoring floats and strings. In addition, It cannot use any built in functions to do so (reverse() and [::-1]). So far, I have
def intvert(lst):
finallst = []
for i in range(len(lst)-1,-1,-1):
if i == type(int):
finallst.append(i)
elif i != type(int):
continue
return finallst
However, this only produces [] as the output
example: (1,g,2.6,2,3,4,h,dfgs,dsgfgdsg,5) becomes (5,4,3,2,1)

A generator is useful for these kinds of tasks. Note it is better to use isinstance versus type. Also you do not have to explicitly check for non-int types if you aren't going to do anything with them.
def intvert(lst):
for i in range(len(lst)-1, -1, -1):
if isinstance(lst[i], int):
yield lst[i]
list(intvert([1, 2, 3, 4]))
# [4, 3, 2, 1]
list(intvert([1, 'test', 2, ['another test'], 3, 4]))
# [4, 3, 2, 1]
Note that the generator function can be optimised further by converting it into a generator expression:
def intvert(lst):
return (lst[i] for i in range(len(lst)-1, -1, -1) if isinstance(lst[i], int))

The reason nothing's getting appended to your list is because your if condition is erroneous. By typing if i == type(int) you're trying to verify if your counter variable "i" (always an integer) is equal to the type of Python's reserved keyword "int"... which doesn't really make sense.
Here's an if condition that should get this working:
if isinstance(lst[i],int):
finallst.append(lst[i])
You need to index into a value in your list (some indexing exercises here). The "isinstance()" method checks the type of the value at the ith position in "lst" to type int. If True, the value is appended to your list.
Fyi: You also don't need the elif or an else block in this case.

def is_int(val):
if type(val) == int:
return True
else:
return False
lis=[1,"aa",2,"b",3,"c"]
lim=[]
for x in lis:
if is_int(x)==True:
lim.append(x)
k=len(lim)
for i in range(k//2):
a=lim[i]
lim[i]=lim[k-1]
lim[k-1]=a
print(lim)
'''this code does so same logic as yours tell me if you didnt understand.i used
type operater you can use something else.'''

To filter your list you can use list comprehensions:
input_list = [1,2.6,'hi',3,'blah',{},99]
filtered_list = [x for x in input_list if type(x) is int]
>>> [1,3,99]
Then you can reverse that list in whatever way you want according to your homework constraints. Normally you'd call .reverse() on the filtered_list but up to you. If list.reverse() is out, I don't know if list.instert() is allowed, but if it is, then you can do:
reversed_filtered_list = []
for i in filtered_list:
reversed_filtered_list.insert(0, i)

Related

Filter function with mapping and lamda

I wrote a function trying to compute and map the list, it works fine for this. But when I try to use a filter to filter out any integer values that are less than 5 in map result and return a list, it gives me an error "TypeError: 'NoneType' object is not iterable", can someone help me with this?
def compute(value):
if type(value) == int or type(value) == float:
return value ** 2
elif type(value) == str:
return value[::-1]
def map_compute(my_list):
print(list(map(compute, my_list)))
it works fine until here for the filter option:
def filter_compute(my_list):
number_list = map_compute(my_list)
new_list = list(filter(lambda x: x > 5, number_list))
print(new_list)
filter_compute(['cup', '321', 2, ['x'], 4])
Want I want is that :
Example: function call:
filter_compute(['cup', '321', 2, ['x'], 4])
Expected returned output:
['puc', '123', None, 16]
Another question is that is there any other way, for example just use lambda to do all the above functions?
WARNING
Before anything else, there is an important matter to address: Why are you checking types? It should be avoided as much as possible, particularly in a situation as simple as this one. Is your program purely for educational purposes?
You ask: Another question is that is there any other way, for example just use lambda to do all the above functions? The answer to that is yes, there are other ways, and no, lambda is not a good one.
Code Review
Let's look at your code.
def compute(value):
if type(value) == int or type(value) == float:
return value ** 2
elif type(value) == str:
return value[::-1]
As I mentioned above, the type checking should be avoided. The name of the function and its parameter need improvement, they're generic, nondescript and provide no useful information.
def map_compute(my_list):
print(list(map(compute, my_list)))
print() prints a value to stdout, you probably want return instead. I also strongly discourage the use of map(). That doesn't even matter, however, since you can get rid of this function entirely.
def filter_compute(my_list):
number_list = map_compute(my_list)
new_list = list(filter(lambda x: x > 5, number_list))
print(new_list)
Again, print() -> return. filter(), much like map, is unidiomatic. This function, too, seems unnecessary, although that depends on its intended purpose. Indeed, that code will crash on your example list, since you're comparing an int (5) to strings and a list.
Solution(ish)
Now, here is how I would rewrite your program:
def comp_value(val_in):
if isinstance(val_in, int) or isinstance(val_in, float):
return val_in ** 2
elif isinstance(val_in, str):
return val_in[::-1]
else:
return None
list_1 = ['cup', '321', 2, ['x'], 4]
list_2 = [comp_value(item) for item in list_1]
list_3 = [item for item in list_2 if item > 5]
print(list_3)
The two superfluous functions are replaced with simple list comprehensions. This code still doesn't make much sense and crashes, of course, the important part is how it is written.
Change
def map_compute(my_list):
print(list(map(compute, my_list)))
to
def map_compute(my_list):
return list(map(compute, my_list))
using the print function will return None object from map_compute, which result the number_list var to be None, and will make your exception, caused by the filter function that want to get an iterable item, but will get None
Below (Using inline condition and list comprehension).
It works but it is not very readable and I think you should avoid using this kind of code.
lst = ['cup', '321', 2, ['x'], 4]
new_lst = [x ** 2 if isinstance(x, (int, float)) else x[::-1] if isinstance(x, str) else None for x in lst]
print(new_lst)
output
['puc', '123', 4, None, 16]

How do I determine whether a container is infinitely recursive and find its smallest unique container?

I was reading Flatten (an irregular) list of lists and decided to adopt it as a Python exercise - a small function I'll occasionally rewrite without referring to the original, just for practice. The first time I tried this, I had something like the following:
def flat(iterable):
try:
iter(iterable)
except TypeError:
yield iterable
else:
for item in iterable:
yield from flatten(item)
This works fine for basic structures like nested lists containing numbers, but strings crash it because the first element of a string is a single-character string, the first element of which is itself, the first element of which is itself again, and so on. Checking the question linked above, I realized that that explains the check for strings. That gave me the following:
def flatter(iterable):
try:
iter(iterable)
if isinstance(iterable, str):
raise TypeError
except TypeError:
yield iterable
else:
for item in iterable:
yield from flatten(item)
Now it works for strings as well. However, I then recalled that a list can contain references to itself.
>>> lst = []
>>> lst.append(lst)
>>> lst
[[...]]
>>> lst[0][0][0][0] is lst
True
So, a string isn't the only type that could cause this sort of problem. At this point, I started looking for a way to guard against this issue without explicit type-checking.
The following flattener.py ensued. flattish() is a version that just checks for strings. flatten_notype() checks whether an object's first item's first item is equal to itself to determine recursion. flatten() does this and then checks whether either the object or its first item's first item is an instance of the other's type. The Fake class basically just defines a wrapper for sequences. The comments on the lines that test each function describe the results, in the form should be `desired_result` [> `undesired_actual_result`]. As you can see, each fails in various ways on Fake wrapped around a string, Fake wrapped around a list of integers, single-character strings, and multiple-character strings.
def flattish(*i):
for item in i:
try: iter(item)
except: yield item
else:
if isinstance(item, str): yield item
else: yield from flattish(*item)
class Fake:
def __init__(self, l):
self.l = l
self.index = 0
def __iter__(self):
return self
def __next__(self):
if self.index >= len(self.l):
raise StopIteration
else:
self.index +=1
return self.l[self.index-1]
def __str__(self):
return str(self.l)
def flatten_notype(*i):
for item in i:
try:
n = next(iter(item))
try:
n2 = next(iter(n))
recur = n == n2
except TypeError:
yield from flatten(*item)
else:
if recur:
yield item
else:
yield from flatten(*item)
except TypeError:
yield item
def flatten(*i):
for item in i:
try:
n = next(iter(item))
try:
n2 = next(iter(n))
recur = n == n2
except TypeError:
yield from flatten(*item)
else:
if recur:
yield item if isinstance(n2, type(item)) or isinstance(item, type(n2)) else n2
else:
yield from flatten(*item)
except TypeError:
yield item
f = Fake('abc')
print(*flattish(f)) # should be `abc`
print(*flattish((f,))) # should be `abc` > ``
print(*flattish(1, ('a',), ('bc',))) # should be `1 a bc`
f = Fake([1, 2, 3])
print(*flattish(f)) # should be `1 2 3`
print(*flattish((f,))) # should be `1 2 3` > ``
print(*flattish(1, ('a',), ('bc',))) # should be `1 a bc`
f = Fake('abc')
print(*flatten_notype(f)) # should be `abc`
print(*flatten_notype((f,))) # should be `abc` > `c`
print(*flatten_notype(1, ('a',), ('bc',))) # should be `1 a bc` > `1 ('a',) bc`
f = Fake([1, 2, 3])
print(*flatten_notype(f)) # should be `1 2 3` > `2 3`
print(*flatten_notype((f,))) # should be `1 2 3` > ``
print(*flatten_notype(1, ('a',), ('bc',))) # should be `1 a bc` > `1 ('a',) bc`
f = Fake('abc')
print(*flatten(f)) # should be `abc` > `a`
print(*flatten((f,))) # should be `abc` > `c`
print(*flatten(1, ('a',), ('bc',))) # should be `1 a bc`
f = Fake([1, 2, 3])
print(*flatten(f)) # should be `1 2 3` > `2 3`
print(*flatten((f,))) # should be `1 2 3` > ``
print(*flatten(1, ('a',), ('bc',))) # should be `1 a bc`
I've also tried the following with the recursive lst defined above and flatten():
>>> print(*flatten(lst))
[[...]]
>>> lst.append(0)
>>> print(*flatten(lst))
[[...], 0]
>>> print(*list(flatten(lst))[0])
[[...], 0] 0
As you can see, it fails similarly to 1 ('a',) bc as well as in its own special way.
I read how can python function access its own attributes? thinking that maybe the function could keep track of every object it had seen, but that wouldn't work either because our lst contains an object with matching identity and equality, strings contain objects that may only have matching equality, and equality isn't enough due to the possibility of something like flatten([1, 2], [1, 2]).
Is there any reliable way (i.e. doesn't simply check known types, doesn't require that a recursive container and its containers all be of the same type, etc.) to check whether a container holds iterable objects with potential infinite recursion, and reliably determine the smallest unique container? If there is, please explain how it can be done, why it is reliable, and how it handles various recursive circumstances. If not, please explain why this is logically impossible.
I don't think there's a reliable way to find out if an arbitrary iterable is infinite. The best we can is to yield primitives infinitely from such an iterable without exhausting the stack, for example:
from collections import deque
def flat(iterable):
d = deque([iterable])
def _primitive(x):
return type(x) in (int, float, bool, str, unicode)
def _next():
x = d.popleft()
if _primitive(x):
return True, x
d.extend(x)
return False, None
while d:
ok, x = _next()
if ok:
yield x
xs = [1,[2], 'abc']
xs.insert(0, xs)
for p in flat(xs):
print p
The above definition of "primitive" is, well, primitive, but that surely can be improved.
The scenario you ask about is very loosely defined. As defined in your question, it is logically impossible "to check whether a container holds iterable objects with potential infinite recursion[.]" The only limit on the scope of your question is "iterable" object. The official Python documentation defines "iterable" as follows:
An object capable of returning its members one at a time. Examples of iterables include all sequence types (such as list, str, and tuple) and some non-sequence types like dict, file objects, and objects of any classes you define with an __iter__() or __getitem__() method. [...]
The key phrase here is "any classes [defined] with an __iter__() or __getitem__() method." This allows for "iterable" objects with members that are generated on demand. For example, suppose that someone seeks to use a bunch of string objects that automatically sort and compare in chronological order based on the time at which the particular string was created. They either subclass str or reimplement its functionality, adding a timestamp associated with each pointer to a timestampedString( ) object, and adjust the comparison methods accordingly.
Accessing a substring by index location is a way of creating a new string, so a timestampedString( ) of len( ) == 1 could legitimately return a timestampedString( ) of len( ) == 1 with the same character but a new timestamp when you access timestampedString( )[0:1]. Because the timestamp is part of the specific object instance, there is no kind of identity test that would say that the two objects are the same unless any two strings consisting of the same character are considered to be the same. You state in your question that this should not be the case.
To detect infinite recursion, you first need to add a constraint to the scope of your question that the container only contain static, i.e. pre-generated, objects. With this constraint, any legal object in the container can be converted to some byte-string representation of the object. A simple way to do this would be to pickle each object in the container as you reach it, and maintain a stack of the byte-string representations that result from pickling. If you allow any arbitrary static object, nothing less than a raw-byte interpretation of the objects is going to work.
However, algorithmically enforcing the constraint that the container only contain static objects presents another problem: it requires type-checking against some pre-approved list of types such as some notion of primitives. Two categories of objects can then be accommodated: single objects of a known-static type (e.g. primitives) and containers for which the number of contained items can be determined in advance. The latter category can then be shown to be finite when that many contained objects have been iterated through and all have been shown to be finite. Containers within the container can be handled recursively. The known-static type single objects are the recursive base-case.
If the container produces more objects, then it violates the definition of this category of object. The problem with allowing arbitrary objects in Python is that these objects can be defined in Python code that can use components written in C code and any other language that C can be linked to. There is no way to evaluate this code to determine if it actually complies with the static requirement.
There's an issue with your test code that's unrelated to the recursive container issue you're trying to solve. The issue is that your Fake class is an iterator and can only be used once. After you iterate over all its values, it will always raise StopIteration when you try to iterate on it again.
So if you do multiple operations on the same Fake instance, you shouldn't expect to get anything be empty output after the first operation has consumed the iterator. If you recreate the iterator before each operation, you won't have that problem (and you can actually try addressing the recursion issue).
So on to that issue. One way to avoid infinite recursion is to maintain a stack with the objects that you're currently nested in. If the next value you see is already on the stack somewhere, you know it's recursive and can skip it. Here's an implementation of this using a list as the stack:
def flatten(obj, stack=None):
if stack is None:
stack = []
if obj in stack:
yield obj
try:
it = iter(obj)
except TypeError:
yield obj
else:
stack.append(obj)
for item in it:
yield from flatten(item, stack)
stack.pop()
Note that this can still yield values from the same container more than once, as long as it's not nested within itself (e.g. for x=[1, 2]; y=[x, 3, x]; print(*flatten(y)) will print 1 2 3 1 2).
It also does recurse into strings, but it will only do so for only one level, so flatten("foo") will yield the letters 'f', 'o' and 'o' in turn. If you want to avoid that, you probably do need the function to be type aware, since from the iteration protocol's perspective, a string is not any different than an iterable container of its letters. It's only single character strings that recursively contain themselves.
What about something like this:
def flat(obj, used=[], old=None):
#This is to get inf. recurrences
if obj==old:
if obj not in used:
used.append(obj)
yield obj
raise StopIteration
try:
#Get strings
if isinstance(obj, str):
raise TypeError
#Try to iterate the obj
for item in obj:
yield from flat(item, used, obj)
except TypeError:
#Get non-iterable items
if obj not in used:
used.append(obj)
yield obj
After a finite number of (recursion) steps a list will contain at most itself as iterable element (Since we have to generate it in finite many steps). That's what we test for with obj==old where obj in an element of old.
The list used keeps track of all elements since we want each element only once. We could remove it but we'd get an ugly (and more importantly not well-defined) behaviour on which elements get yield how often.
Drawback is that we store the entire list at the end in the list used...
Testing this with some lists seems to work:
>> lst = [1]
>> lst.append(lst)
>> print('\nList1: ', lst)
>> print([x for x in flat(lst)])
List1: [1, [...]]
Elements: [1, [1, [...]]]
#We'd need to reset the iterator here!
>> lst2 = []
>> lst2.append(lst2)
>> lst2.append((1,'ab'))
>> lst2.append(lst)
>> lst2.append(3)
>> print('\nList2: ', lst2)
>> print([x for x in flat(lst2)])
List2: [[...], (1, 'ab'), [1, [...]], 3]
Elements: [[[...], (1, 'ab'), [1, [...]], 3], 1, 'ab', [1, [...]], 3]
Note: It actually makes sense that the infinite lists [[...], (1, 'ab'), [1, [...]], 3] and [1, [...]] are considered as elements since these actually contain themselves but if that's not desired one can comment out the first yield in the code above.
Just avoid flattening recurring containers. In the example below keepobj keeps track of them and keepcls ignores containers of a certain type. I believe this works down to python 2.3.
def flatten(item, keepcls=(), keepobj=()):
if not hasattr(item, '__iter__') or isinstance(item, keepcls) or item in keepobj:
yield item
else:
for i in item:
for j in flatten(i, keepcls, keepobj + (item,)):
yield j
It can flatten circular lists like lst = [1, 2, [5, 6, {'a': 1, 'b': 2}, 7, 'string'], [...]] and keep some containers like strings and dicts un-flattened.
>>> list(flatten(l, keepcls=(dict, str)))
[1, 2, 5, 6, {'a': 1, 'b': 2}, 7, 'string', [1, 2, [5, 6, {'a': 1, 'b': 2}, 7, 'string'], [...]]]
It also works with the following case:
>>> list(flatten([[1,2],[1,[1,2]],[1,2]]))
[1, 2, 1, 1, 2, 1, 2]
You may want to keep some default classes in keepcls to make calling
the function more terse.

TypeError: 'list' object cannot be interpreted as an integer

The playSound function is taking a list of integers, and is going to play a sound for every different number. So if one of the numbers in the list is 1, 1 has a designated sound that it will play.
def userNum(iterations):
myList = []
for i in range(iterations):
a = int(input("Enter a number for sound: "))
myList.append(a)
return myList
print(myList)
def playSound(myList):
for i in range(myList):
if i == 1:
winsound.PlaySound("SystemExit", winsound.SND_ALIAS)
I am getting this error:
TypeError: 'list' object cannot be interpreted as an integer
I have tried a few ways to convert the list to integers. I am not too sure what I need to change. I am sure that there is a more efficient way of doing this. Any help would be very greatly appreciated.
Error messages usually mean precisely what they say. So they must be read very carefully. When you do that, you'll see that this one is not actually complaining, as you seem to have assumed, about what sort of object your list contains, but rather about what sort of object it is. It's not saying it wants your list to contain integers (plural)—instead, it seems to want your list to be an integer (singular) rather than a list of anything. And since you can't convert a list into a single integer (at least, not in a way that is meaningful in this context) you shouldn't be trying.
So the question is: why does the interpreter seem to want to interpret your list as an integer? The answer is that you are passing your list as the input argument to range, which expects an integer. Don't do that. Say for i in myList instead.
For me i was getting this error because i needed to put the arrays in paratheses. The error is a bit tricky in this case...
ie. concatenate((a, b)) is right
not concatenate(a, b)
hope that helps.
The error is from this:
def playSound(myList):
for i in range(myList): # <= myList is a list, not an integer
You cannot pass a list to range which expects an integer. Most likely, you meant to do:
def playSound(myList):
for list_item in myList:
OR
def playSound(myList):
for i in range(len(myList)):
OR
def playSound(myList):
for i, list_item in enumerate(myList):
range is expecting an integer argument, from which it will build a range of integers:
>>> range(10)
range(0, 10)
>>> list(range(10))
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>>
Moreover, giving it a list will raise a TypeError because range will not know how to handle it:
>>> range([1, 2, 3])
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'list' object cannot be interpreted as an integer
>>>
If you want to access the items in myList, loop over the list directly:
for i in myList:
...
Demo:
>>> myList = [1, 2, 3]
>>> for i in myList:
... print(i)
...
1
2
3
>>>
remove the range.
for i in myList
range takes in an integer. you want for each element in the list.
You should do this instead:
for i in myList:
# etc.
That is, remove the range() part. The range() function is used to generate a sequence of numbers, and it receives as parameters the limits to generate the range, it won't work to pass a list as parameter. For iterating over the list, just write the loop as shown above.
since it's a list it cannot be taken directly into range function as the singular integer value of the list is missing.
use this
for i in range(len(myList)):
with this, we get the singular integer value which can be used easily
In playSound(), instead of
for i in range(myList):
try
for i in myList:
This will iterate over the contents of myList, which I believe is what you want. range(myList) doesn't make any sense.
def userNum(iterations):
myList = []
for i in range(iterations):
a = int(input("Enter a number for sound: "))
myList.append(a)
print(myList) # print before return
return myList # return outside of loop
def playSound(myList):
for i in range(len(myList)): # range takes int not list
if i == 1:
winsound.PlaySound("SystemExit", winsound.SND_ALIAS)
n=input().split()
ar=[]
for i in n:
if i not in ar:
ar.append(i)
print(*ar)
We usually pass string as a integer... for that we have to type "n"
when we facing like this error:
TypeError: st object cannot be interpreted as an st
it usually because we use X instead len(X) in for loops
#error
for i in range(df.index):
pass
#currect
for i in range( len(df.index) ):
pass
list cannot be interpreted as an integer when using range
for i in range(list): -> will not work
for i in list: -> will work

How to uniformly handle A LIST or A LIST OF LIST (multi-dimensional list)

Without any heavy libraries such as numpy, I want to uniformly handle a single list or multi-dimensional list in my code. For example, the function sum_up(list_or_matrix) should
return 6 for argument [1, 2, 3] and return 9 for [[1, 2, 3], [1, 2, 0]].
My question is:
1. Can I code in a way without explicitly detecting the dimension of my input such as by isinstance(arg[0], (tuple, list))?
2. If I have to do so, is there any elegant way of detecting the dimension of a list (of list of list ...), e.g. recursively?
As many users suggested you can always use dict instead of list for any-dimensinal collection. Dictionaries are accepting tuples as arguments as they are hashable. So you can easy fill-up your collection like
>>> m = {}
>>> m[1] = 1
>>> m[1,2] = 12
>>> m[1,2,"three",4.5] = 12345
>>> sum(m.values()) #better use m.itervalues() in python 2.*
12358
You can solve this problem using recursion, like this:
#!/usr/bin/env python
def sum(seq_or_elem):
if hasattr(seq_or_elem, '__iter__'):
# We were passed a sequence so iterate over it, summing the elements.
total = 0
for i in seq_or_elem:
total += sum(i)
return total
else:
# We were passed an atomic element, the sum is identical to the passed value.
return seq_or_elem
Test:
>>> print(sum([1, 2, [3, [4]], [], 5]))
15
Well I dont see a way if you are planning to use a single function to sum up your list like sum_up(list_or_matrix).
If you are having a list of lists I would only imagine you need to loop through the list to find out if its a 1-D or a 2-D list. Anyway whats wrong in looping?
def sum_up(matrix):
2DMatrix = False
for item in matrix:
if type(item) == list:
2DMatrix = True
if(2DMatrix):
//sum 2d matrix
else:
//sum 1d matrix
A simple way to sum up a matrix is as follow:
def sum_up(matrix):
if isinstance(matrix, (tuple, list)):
return sum(matrix)
else:
return sum((sum(x) for x in matrix))
The 'else' branch uses list comprehension, a powerful and quick tool.
You could sum recursively until you have a scalar value:
def flatten(x):
if isinstance(x, list):
return sum(map(flatten, x))
return x
Note: you can use collections.Iterable (or another base class) instead of list, depending on what you want to flatten.

Python: add a value to the end of the inner-most right nested list

What I'm trying to do, is, given a list with an arbitrary number of other nested lists, recursively descend through the last value in the nested lists until I've reached the maximum depth, and then append a value to that list. An example might make this clearer:
>>> nested_list1 = [1, 2, 3, [4, 5, 6]]
>>> last_inner_append(nested_list1, 7)
[1, 2, 3, [4, 5, 6, 7]]
>>> nested_list2 = [1, 2, [3, 4], 5, 6]
>>> last_inner_append(nested_list2, 7)
[1, 2, [3, 4], 5, 6, 7]
The following code works, but it seems excessively tricky to me:
def add_to_inner_last(nested, item):
nest_levels = [nested]
try:
nest_levels.append(nested[-1])
except IndexError: # The empty list case
nested.append(item)
return
while type(nest_levels[-1]) == list:
try:
nest_levels.append(nest_levels[-1][-1])
except IndexError: # The empty inner list case
nest_levels[-1].append(item)
return
nest_levels[-2].append(item)
return
Some things I like about it:
It works
It handles the cases of strings at the end of lists, and the cases of empty lists
Some things I don't like about it:
I have to check the type of objects, because strings are also indexable
The indexing system feels too magical--I won't be able to understand this tomorrow
It feels excessively clever to use the fact that appending to a referenced list affects all references
Some general questions I have about it:
At first I was worried that appending to nest_levels was space inefficient, but then I realized that this is probably just a reference, and a new object is not created, right?
This code is purely side effect producing (It always returns None). Should I be concerned about that?
Basically, while this code works (I think...), I'm wondering if there's a better way to do this. By better I mean clearer or more pythonic. Potentially something with more explicit recursion? I had trouble defining a stopping point or a way to do this without producing side effects.
Edit:
To be clear, this method also needs to handle:
>>> last_inner_append([1,[2,[3,[4]]]], 5)
[1,[2,[3,[4,5]]]]
and:
>>> last_inner_append([1,[2,[3,[4,[]]]]], 5)
[1,[2,[3,[4,[5]]]]]
How about this:
def last_inner_append(x, y):
try:
if isinstance(x[-1], list):
last_inner_append(x[-1], y)
return x
except IndexError:
pass
x.append(y)
return x
This function returns the deepest inner list:
def get_deepest_list(lst, depth = 0):
deepest_list = lst
max_depth = depth
for li in lst:
if type(li) == list:
tmp_deepest_list, tmp_max_depth = get_deepest_list(li, depth + 1)
if max_depth < tmp_max_depth: # change to <= to get the rightmost inner list
max_depth = tmp_max_depth
deepest_list = tmp_deepest_list
return deepest_list, max_depth
And then use it as:
def add_to_deepest_inner(lst, item):
inner_lst, depth = get_deepest_list(lst)
inner_lst.append(item)
Here is my take:
def last_inner_append(cont, el):
if type(cont) == list:
if not len(cont) or type(cont[-1]) != list:
cont.append(el)
else:
last_inner_append(cont[-1], el)
I think it's nice and clear, and passes all your tests.
It is also pure side-effect; if you want to change this, I suggest you go with BasicWolf's approach and create a 'selector' and an 'update' function, where the latter uses the former.
It's the same recursion scheme as Phil H's, but handles empty lists.
I don't think there is a good way around the two type tests, however you approach them (e.g. with 'type' or checking for 'append'...).
You can test if append is callable, rather than using try/catch, and recursing:
def add_to_inner_last(nested, item):
if callable(nested,append):
if callable(nested[-1],append):
return add_to_inner_last(nested[-1],item)
else:
nested.append(item)
return true
else:
return false
It's slightly annoying to have to have two callable tests, but the alternative is to pass a reference to the parent as well as the child.
def last_inner_append(sequence, element):
def helper(tmp, seq, elem=element):
if type(seq) != list:
tmp.append(elem)
elif len(seq):
helper(seq, seq[-1])
else:
seq.append(elem)
helper(sequence, sequence)

Categories