Python Function returning string with quotes - python

I have a function and returning three values, The return value for my function is
(1, 3, "<class 'int'>")
while I want to return
(1, 3, <class 'int'>)
How do I remove the quotes from my return value.str(type(element)) is the value which is returning the 3rd value
def is_list_permutation(L1, L2):
L1set = set(L1)
L2set = set(L2)
count = 0
element = ''
if L1 == [] and L2 == []:
return(None,None,None)
elif len(L1) == len(L2) and L1set == L2set:
for a in L1:
if L1.count(a) == L2.count(a):
if L1.count(a) > count:
count = L1.count(a)
element = a
return(element,count,str(type(element)))
else:
return False
break
else:
return False
so if i give
L1 = [1, 'b', 1, 'c', 'c', 1]
L2 = ['c', 1, 'b', 1, 1, 'c']
then the result is (1, 3, "<class 'int'>") while I want (1, 3, <class 'int'>)

Your function is returning a tuple with three elements: element, count and the string representation of the type of element. At no point, this has any string representation which you’re seeing there. You just get back a tuple.
Now, when you print that tuple, then the print function will actually try to convert it into a string. For a tuple, this string representation is defined to be a set of parentheses (), and the repr() string representation of each tuple element inside.
For numbers, this will look fine since repr(5) happens to be the string '5'. But for strings, the repr will add quotes to make sure that the return string would be valid Python code:
>>> repr('foo')
"'foo'"
>>> print(repr('foo'))
'foo'
Now, when you say, you want the result without those quotes, you have to think about what that actually means. You could easily format the result string yourself. For example like this:
return '({0}, {1}, {2})'.format(element, count, str(type(element)))
This will return a string that would look like the way you want.
However, by doing that, you also lose the information you had when you returned a tuple. Now, you just return a string that has no information about the actual source values. So you cannot take the count value out without parsing the string again.
So, think about what you want to do: Do you actually just want something nice to print, or do you actually want to get those three values individually as a return value to be able to use them for something else afterwards?
You could always consider printing the text in the desired format later, if you already have the return values as a tuple…
Btw.: Note that when not calling str(type(element)) but just type(element), you would get back a type element (instead of a string). And the repr() of a type element happens to be exactly what you would want to have. So as a quick fix, you could always get rid of that str() call there.

There will always be some quotes around it because it is a string!
If you want to return None when the string is "" then you could do this:
x = str(type(s))
return(element, count, x if x else None)

Related

Reversing ONLY integers in list without slice or reverse()

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)

How to create a tuple of an empty tuple in Python?

How can I create a tuple consisting of just an empty tuple, i.e. (())? I have tried tuple(tuple()), tuple(tuple(tuple())), tuple([]) and tuple(tuple([])) which all gave me ().
The reason that I use such a thing is as follows: Assume you have n bags with m items. To represent a list of items in a bag, I use a tuple of length n where each element of that tuple is a representative for a bag. A bag might be empty, which is labeled by (). Now, at some initial point, I have just one bag with empty items!
The empty tuple is () (or the more-verbose and slower tuple()), and a tuple with just one item (such as the integer 1), called a singleton (see here and here) is (1,). Therefore, the tuple containing only the empty tuple is
((),)
Here are some results showing that works:
>>> a=((),)
>>> type(a)
<type 'tuple'>
>>> len(a)
1
>>> a[0]
()
>>> type(a[0])
<type 'tuple'>
>>> len(a[0])
0
I'm not surprised this (()) didn't work, since the outer parentheses get interpreted as that - parentheses. So (()) == (), just like (2) == 2. This should work, however:
((),)
An empty tuple:
my_tuple = ()
A tuple with 1 string:
my_tuple = ('foo',)
A tuple with 2 strings:
my_tuple = ('foo', 'bar')
A tuple with 1 empty tuple:
my_tuple = ((),)
A tuple with 2 empty tuples:
my_tuple = ((), ())
in Python 2, tuple() is the only genuine empty tuple, but (), and ((),) create a tuple of length 1 that contains a tuple of length 0 - but not a tuple of length zero itself.
If you want an answer to "how do I create an empty (or zero length) tuple.... I found this post with the search "how to create an empty tuple", then realized this was not the same question, but could be mistaken for that question (as the search does), so I though I would provide the answer to :
How do you simply create an empty tuple?
the original question could mislead you, as the original answers are almost good enough as an empty tuple, but do fail one test.
(), will create an 'empty' tuple as suggested in previous answers with ((),) which will also work, as will ((( ((( (),))) ))) in fact you can use any number of outer brackets you choose, they just work as brackets. However, python, when printing a tuple, does add one set of outer brackets.
empty brackets is a non-standard representation of 'no value' and adding the trailing comma makes a tuple from 'no value'. But it is a tuple with a 'no value' entry, not an empty tuple.
Note: This is not a zero length tuple, as the other examples have also shown. The outer tuple is a tuple with one value, just that value has itself, is the empty tuple. So this creates an empty tuple inside another tuple, and the other tuple is not empty. For a true empty tuple by itself, use tuple() although the (), behaves some what similar, it is not quite correct.
>>> a = (),
>>> type(a)
<class 'tuple'>
>>> len(a)
1
>>> a
((),)
>>> len(a[0]) # the inside tuple is empty, just not the outside one
0
Similarly, for a tuple of length 1 but with a value (of zero in the case of b, and "" for the example with c)
>>> b = 0,
>>> type(b)
<class 'tuple'>
>>> len(b)
1
>>>b
(0,)
# now with an empty string
>>> c = "",
>>> type(c)
<class 'tuple'>
>>> len(c)
1
>>>c
('',)
>>> len (c[0]) # same len of c[0] as with 'empty' tuple
0
So the outer brackets are included for displaying a tuple, but not actually part of the tuple, nor needed for creating the tuple.
However all these brackets methods are not a real empty at the outer level, which is something that also has use cases.
>>> a = ((),) # extra brackets just to show same as other answers
>>> len(a)
1
>>> if a:
print("not empty")
not empty
>>> e = tuple()
>>> len(e)
0
>>> type(e)
<class 'tuple'>
>>> if e:
print("not empty")
>>> # note...did not print...so e acts as false as an empty tuple should
So if you really need a genuine empty tuple, use tuple(), but if near enough is all you need, you can use (), or ((),)
In the general case, it's the commas that make tuples, not the parentheses. Things become confusing in the case of empty tuples because a standalone comma is syntactically incorrect. So for the special case of an empty tuple, the "it is commas that make tuples" rule does not apply, and the special case () syntax is used instead.

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.

Why is a[:]=1 fundamentally different to a[:]='1'?

Please consider the two snippets of code (notice the distinction between string and integer):
a = []
a[:] = '1'
and
a = []
a[:] = 1
In the first case a is ['1']. In the second, I get the error TypeError: can only assign an iterable. Why would using '1' over 1 be fundamentally different here?
Assigning to a slice requires an iterable on the right-hand side.
'1' is iterable, while 1 is not. Consider the following:
In [7]: a=[]
In [8]: a[:]='abc'
The result is:
In [9]: a
Out[9]: ['a', 'b', 'c']
As you can see, the list gets each character of the string as a separate item. This is a consequence of the fact that iterating over a string yields its characters.
If you want to replace a range of a's elements with a single scalar, simply wrap the scalar in an iterable of some sort:
In [11]: a[:]=(1,) # single-element tuple
In [12]: a
Out[12]: [1]
This also applies to strings (provided the string is to be treated as a single item and not as a sequence of characters):
In [17]: a[:]=('abc',)
In [18]: a
Out[18]: ['abc']
'1' is a string, but it is iterable. It is like a list of characters. a[:]='1' replaces the contents of the list a with the content of the string '1'. But 1 is an integer.
Python does not change the type.
Example:
print bool(1=='1') # --> False

Why doesn't list have safe "get" method like dictionary?

Why doesn't list have a safe "get" method like dictionary?
>>> d = {'a':'b'}
>>> d['a']
'b'
>>> d['c']
KeyError: 'c'
>>> d.get('c', 'fail')
'fail'
>>> l = [1]
>>> l[10]
IndexError: list index out of range
Ultimately it probably doesn't have a safe .get method because a dict is an associative collection (values are associated with names) where it is inefficient to check if a key is present (and return its value) without throwing an exception, while it is super trivial to avoid exceptions accessing list elements (as the len method is very fast). The .get method allows you to query the value associated with a name, not directly access the 37th item in the dictionary (which would be more like what you're asking of your list).
Of course, you can easily implement this yourself:
def safe_list_get (l, idx, default):
try:
return l[idx]
except IndexError:
return default
You could even monkeypatch it onto the __builtins__.list constructor in __main__, but that would be a less pervasive change since most code doesn't use it. If you just wanted to use this with lists created by your own code you could simply subclass list and add the get method.
This works if you want the first element, like my_list.get(0)
>>> my_list = [1,2,3]
>>> next(iter(my_list), 'fail')
1
>>> my_list = []
>>> next(iter(my_list), 'fail')
'fail'
I know it's not exactly what you asked for but it might help others.
Probably because it just didn't make much sense for list semantics. However, you can easily create your own by subclassing.
class safelist(list):
def get(self, index, default=None):
try:
return self.__getitem__(index)
except IndexError:
return default
def _test():
l = safelist(range(10))
print l.get(20, "oops")
if __name__ == "__main__":
_test()
Instead of using .get, using like this should be ok for lists. Just a usage difference.
>>> l = [1]
>>> l[10] if 10 < len(l) else 'fail'
'fail'
Credits to jose.angel.jimenez and Gus Bus.
For the "oneliner" fans…
If you want the first element of a list or if you want a default value if the list is empty try:
liste = ['a', 'b', 'c']
value = (liste[0:1] or ('default',))[0]
print(value)
returns a
and
liste = []
value = (liste[0:1] or ('default',))[0]
print(value)
returns default
Examples for other elements…
liste = ['a', 'b', 'c']
print(liste[0:1]) # returns ['a']
print(liste[1:2]) # returns ['b']
print(liste[2:3]) # returns ['c']
print(liste[3:4]) # returns []
With default fallback…
liste = ['a', 'b', 'c']
print((liste[0:1] or ('default',))[0]) # returns a
print((liste[1:2] or ('default',))[0]) # returns b
print((liste[2:3] or ('default',))[0]) # returns c
print((liste[3:4] or ('default',))[0]) # returns default
Possibly shorter:
liste = ['a', 'b', 'c']
value, = liste[:1] or ('default',)
print(value) # returns a
It looks like you need the comma before the equal sign, the equal sign and the latter parenthesis.
More general:
liste = ['a', 'b', 'c']
f = lambda l, x, d: l[x:x+1] and l[x] or d
print(f(liste, 0, 'default')) # returns a
print(f(liste, 1, 'default')) # returns b
print(f(liste, 2, 'default')) # returns c
print(f(liste, 3, 'default')) # returns default
Tested with Python 3.6.0 (v3.6.0:41df79263a11, Dec 22 2016, 17:23:13)
Try this:
>>> i = 3
>>> a = [1, 2, 3, 4]
>>> next(iter(a[i:]), 'fail')
4
>>> next(iter(a[i + 1:]), 'fail')
'fail'
A reasonable thing you can do is to convert the list into a dict and then access it with the get method:
>>> my_list = ['a', 'b', 'c', 'd', 'e']
>>> my_dict = dict(enumerate(my_list))
>>> print my_dict
{0: 'a', 1: 'b', 2: 'c', 3: 'd', 4: 'e'}
>>> my_dict.get(2)
'c'
>>> my_dict.get(10, 'N/A')
So I did some more research into this and it turns out there isn't anything specific for this. I got excited when I found list.index(value), it returns the index of a specified item, but there isn't anything for getting the value at a specific index. So if you don't want to use the safe_list_get solution which I think is pretty good. Here are some 1 liner if statements that can get the job done for you depending on the scenario:
>>> x = [1, 2, 3]
>>> el = x[4] if len(x) > 4 else 'No'
>>> el
'No'
You can also use None instead of 'No', which makes more sense.:
>>> x = [1, 2, 3]
>>> i = 2
>>> el_i = x[i] if len(x) == i+1 else None
Also if you want to just get the first or last item in the list, this works
end_el = x[-1] if x else None
You can also make these into functions but I still liked the IndexError exception solution. I experimented with a dummied down version of the safe_list_get solution and made it a bit simpler (no default):
def list_get(l, i):
try:
return l[i]
except IndexError:
return None
Haven't benchmarked to see what is fastest.
Dictionaries are for look ups. It makes sense to ask if an entry exists or not. Lists are usually iterated. It isn't common to ask if L[10] exists but rather if the length of L is 11.
If you
want a one liner,
prefer not having try / except in your happy code path where you needn't, and
want the default value to be optional,
you can use this:
list_get = lambda l, x, d=None: d if not l[x:x+1] else l[x]
Usage looks like:
>>> list_get(['foo'], 4) == None
True
>>> list_get(['hootenanny'], 4, 'ho down!')
'ho down!'
>>> list_get([''], 0)
''
For small index values you can implement
my_list.get(index, default)
as
(my_list + [default] * (index + 1))[index]
If you know in advance what index is then this can be simplified, for example if you knew it was 1 then you could do
(my_list + [default, default])[index]
Because lists are forward packed the only fail case we need to worry about is running off the end of the list. This approach pads the end of the list with enough defaults to guarantee that index is covered.
This isn't an extremely general-purpose solution, but I had a case where I expected a list of length 3 to 5 (with a guarding if), and I was breaking out the values to named variables. A simple and concise way I found for this involved:
foo = (argv + [None, None])[3]
bar = (argv + [None, None])[4]
Now foo and bar are either the 4th and 5th values in the list, or None if there weren't that many values.
Your usecase is basically only relevant for when doing arrays and matrixes of a fixed length, so that you know how long they are before hand. In that case you typically also create them before hand filling them up with None or 0, so that in fact any index you will use already exists.
You could say this: I need .get() on dictionaries quite often. After ten years as a full time programmer I don't think I have ever needed it on a list. :)

Categories