Python list append multiple elements - python

I want to append multiple elements to my list at once. I tried this
>>> l = []
>>> l.append('a')
>>> l
['a']
>>> l.append('b').append('c')
Traceback (most recent call last):
File "<pyshell#10>", line 1, in <module>
l.append('b').append('c')
AttributeError: 'NoneType' object has no attribute 'append'
>>>
how can I append 'b' and 'c' at once?

The method append() works in place. In other words, it modifies the list, and doesn't return a new one.
So, if l.append('b') doesn't return anything (in fact it returns None), you can't do:
l.append('b').append('c')
because it will be equivalent to
None.append('c')
Answering the question: how can I append 'b' and 'c' at once?
You can use extend() in the following way:
l.extend(('b', 'c'))

Use list.extend:
>>> l = []
>>> l.extend(('a', 'b'))
>>> l
['a', 'b']
Note that similar to list.append, list.extend also modifies the list in-place and returns None, so it is not possible to chain these method calls.
But when it comes to string, their methods return a new string. So, we can chain methods call on them:
>>> s = 'foobar'
>>> s.replace('o', '^').replace('a', '*').upper()
'F^^B*R'

l = []
l.extend([1,2,3,4,5])
There is a method for your purpose.

Related

Lambda as iterator in Python returns function object on first iteration

I have this code snippet which I can't understand:
>>> l = lambda: -4, 'c', 0
>>> i = iter(l)
>>> i
<tuple_iterator object at 0x00700CD0>
>>> next(i)
<function <lambda> at 0x0070A4F8>
>>> next(i)
'c'
>>> next(i)
0
>>> next(i)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
>>>
Why is it returning lambda object on first iteration, instead of -4?
I think you might have misunderstood what l is.
l is a tuple of 3 elements:
a lambda that returns -4
the string c
the integer 0
When you create an iterator iterating through the tuple, of course the first call to next is going to give you a lambda!
Maybe you meant to call the lambda:
next(i)()
Or maybe you meant to declare l like this:
l = lambda: (-4, 'c', 0) # you might think you don't need the parentheses, but you do
A lambda that returns a tuple.
And then do iter(l()).
When you do this :
>>> l = lambda: -4, 'c', 0
l is actually a tuple containing first item as a lambda function, second item a string and third item an integer.
It is equivalent to the following :
>>> l = (lambda: -4, 'c', 0)
If you want to get access to the lambda function which returns -4, you should try this :
>>> i = iter(l)
>>> next(i)()
-4
But note that next(i)() works only with callable(lambda, functions etc) objects. If you use next(i)() with a string object python will raise TypeError: 'str' object is not callable. So always check if the item is callable. ie,
i = iter(l)
item = next(i)
if callable(item):
print(item())
else:
print(item)

check that x in list.remove(y) in one line

What is the way to make list.remove() behave as a list on this same line to perform operations such as element searches or indexing (i.e. make iterable or subscriptable)? E.g. I am trying the above which are not working.
myList = ['a', 'b', 'c']
'a' in myList.remove('b')
'a' in list(myList.remove('c'))
Error:
Traceback (most recent call last):
File "<pyshell#83>", line 1, in <module>
'a' in list(myList.remove('c'))
TypeError: 'NoneType' object is not iterable
Edit after first responses.
What I am trying to do is the following.
myList = ['a', 'b', 'c']
if 'a' in myList:
myList.remove('b')
if 'c' in myList:
...
So I am wondering if I could this in the same statement
Hope this will do
if 'a' in your_list and 'c' in set(your_list) - {'b'}:
# some code
pass
I am using sets here. Note that after the statement 'b' still will be in the list.
But from a logical point of view I prefer to test for this
{'a', 'c'} <= set(your_list) and 'b' != 'c'
remove edits the list in place and returns None, so what you're doing won't work. You need a function that returns a new list with that element removed. If you really need a one-liner:
'a' in [x for x in myList if x is not 'b']
list.remove() function does not return anything.
So you can check in two steps:
myList = ['a', 'b', 'c']
myList.remove('b')
if 'a' in myList:
#Some logic
you can quickly check if b is in the list and remove it first
myList.remove('b') if 'b' in myList else None
then using:
'a' in myList
To check if 'a' is in the list (with remove 'b')

How to use a list of strings [which may contain any character] as keys? [duplicate]

This question already has answers here:
Why can't I use a list as a dict key in python?
(11 answers)
Closed 4 months ago.
Basically I "understand" the ambiguity with using other containers as keys. - do you compare the reference or do you compare the values in it (and how deep).
Well can't you just specialize a list and make a custom comparison/hash operator? As I know in my application I wish to compare the list-of-strings by the value of the strings (and relative ordering of course).
So how would I go about writing a custom hash for these kind of lists? Or in another way - how would I stringify the list without leading to the ambiguity that you get from introducing delimiters (that delimiter might be part of the string)?!
Regarding this question: https://wiki.python.org/moin/DictionaryKeys
There it is said directly that lists can't be used;
That said, the simple answer to why lists cannot be used as dictionary keys is that lists do not provide a valid hash method. Of course, the obvious question is, "Why not?"
So I am writing this to question if there's a way to make lists hashable; and how I would make a satisfactory Hash method.
As an example of why I would want this, see the code below:
namelist = sorted(["Jan", "Frank", "Kim"])
commonTrait = newTraits()
traitdict = {}
traitdict[namelist] = commonTrait;
//later I wish to retrieve it:
trait = traitdict[sorted(["Jan", "Frank", "Kim"])]
In this direct use I have in mind the "ordering of the list" doesn't really matter (sorting is just done in above code to make the lists always equal if they hold the same content).
If you need to use a collection of strings as a dictionary key, you have 2 obvious choices: if the order matters, use tuple:
>>> d[tuple(['foo', 'bar'])] = 'baz'
>>> d['foo', 'bar']
baz
>>> d['bar', 'foo']
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
KeyError: ('bar', 'foo')
If the order must not matter, use frozenset:
>>> d[frozenset(['foo', 'bar'])] = 'baz'
>>> d[frozenset(['bar', 'foo'])]
'baz'
>>> d[frozenset(['bar', 'foo', 'bar'])]
'baz'
If the count matters but ordering does not, use sorted with a tuple:
>>> d[tuple(sorted(['foo', 'bar']))] = 'baz'
>>> d[tuple(sorted(['bar', 'foo']))]
'baz'
>>> d[tuple(sorted(['bar', 'foo', 'bar']))
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
KeyError: ('bar', 'bar', 'foo')
Unlike with Perl hashes, or JavaScript object properties, you do not need to stringify dictionary keys in Python.
Now, concerning the mutable list not being hashable: The Python dictionary implementation uses the hashtable structure. It specifically requires and assumes of the key that:
the key implements the __hash__ method that returns an integer
that the integers should be as widely spread in the output range, and uniformly mapped, as possible.
that __hash__ method returns an unchanging number for the same object
a == b implies that a.__hash__() == b.__hash__()
A list is not usable as a dictionary key, as:
>>> [].__hash__()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'NoneType' object is not callable
The list class cannot provide a __hash__ method that could satisfy all the requirements simultaneously a == b needs to imply that a.__hash__() == b.__hash__().
(It *could provide an implementation that returns 0 for each list, and it would work correctly then, but it would refute the use of hashing altogether, as all lists would be mapped to the same slot in the dictionary, as the hash codes would break the rule 2).
It is also not possible to create a __hash__ method for a list:
>>> [].__hash__ = lambda x: 0
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'list' object attribute '__hash__' is read-only
Of course we can always see what would happen if list would have the __hash__ method - we create a subclass of list and provide __hash__ there; the obvious implementation for the hash code would be that of the tuple():
>>> class hashablelist(list):
... def __hash__(self):
... return hash(tuple(self))
...
>>> x = hashablelist(['a', 'b', 'c'])
>>> y = hashablelist(['a', 'b', 'd'])
>>> d = {}
>>> d[x] = 'foo'
>>> d[y] = 'bar'
>>> d.items()
[(['a', 'b', 'c'], 'foo'), (['a', 'b', 'd'], 'bar')]
>>> y[2] = 'c'
>>> d.items()
[(['a', 'b', 'c'], 'foo'), (['a', 'b', 'c'], 'bar')]
>>> del d[x]
>>> del d[y]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
KeyError: ['a', 'b', 'c']
>>> d.items()
[(['a', 'b', 'c'], 'bar')]
>>> x in d
False
>>> y in d
False
>>> x in d.keys()
True
>>> y in d.keys()
True
The code shows that we just managed to get a broken dictionary as a result - there is no way of accessing or removing the ['a', 'b', 'c'] -> 'bar' pair directly by the key, even though it is visible in .keys(), .values() and .items().

Why can't I call del [:] on a dict?

What's going on here?
>>> a = {1: "a", 2: "b"}
>>> del a[1]
>>> a
{2: 'b'}
>>> a = {1: "a", 2: "b"}
>>> del a[:]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unhashable type
>>> a.clear()
>>> a
{}
Why must I call dict.clear?
a[:] is a special case of a slicing operation, which is defined only for sequences. It is a short form of a[0:len(a)]. A dict is not a sequence.
a[:] is a quick way to make a shallow copy of a list (and tuple). See towards the bottom of the docs for clarification on different types of copying.
Thus, it would reason to say that del a[:] on a dictionary doesn't really make much sense.
If you want to delete the entire dictionary, you can simply do del a
>>> a = {1: "a", 2: "b"}
>>> del a
>>> a
Traceback (most recent call last):
File "<pyshell#8>", line 1, in <module>
a
NameError: name 'a' is not defined
>>>
The reason is rather simple: It isn't defined. There's no reason why it couldn't work, if someone put the effort into catching the empty slice and delegating to the appropriate method.
On the other hand, this would violate a symmetry principle with assigning and getting the same slice, so probably would not gain acceptance.
If this functionality is crucial to your system, you could subclass the dict builtin to add this function.
import sys
class newdict(dict):
def __delslice__(self,i,j):
if i==0 and j==sys.maxint:
self.clear()
else:
dict.__delslice__(self,i,j)
When you're doing del a[1] you're not deleting the first element in dictionary, you're deleting the element with key 1. Dictionary does not have any ordering and thus slicing algorigthm (in this case at least).
That's why with the dict a = {1 : 'one', 5 : 'five'} you can do del a[1] and cannot do del a[2] or del a[0]

Appending item to lists within a list comprehension

I have a list, let's say, a = [[1,2],[3,4],[5,6]]
I want to add the string 'a' to each item in the list a.
When I use:
a = [x.append('a') for x in a]
it returns [None,None,None].
But if I use:
a1 = [x.append('a') for x in a]
then it does something odd.
a, but not a1 is [[1,2,'a'],[3,4,'a'],[5,6,'a']].
I don't understand why the first call returns [None, None, None] nor why the second changes on a instead of a1.
list.append mutates the list itself and returns None. List comprehensions are for storing the result, which isn't what you want in this case if you want to just change the original lists.
>>> x = [[1, 2], [3, 4], [5, 6]]
>>> for sublist in x:
... sublist.append('a')
...
>>> x
[[1, 2, 'a'], [3, 4, 'a'], [5, 6, 'a']]
As others have said, append mutates the list itself and you shouldn't assign it to a variable. Executing it changes it's data, effectively updating everyone pointing at it.
But, there's a trick I use when I want to do stuff in a functional* way while mutating existing objects (rather than constructing new ones, in this case using a=[x + ['a'] for x in a], or specifically the x + ['a']).
So, if you're brave enough you can also do this:
>>> a=[[1,2],[3,4],[5,6]]
>>> a=[x.append('a') or x for x in a]
>>> a
[[1, 2, 'a'], [3, 4, 'a'], [5, 6, 'a']]
This works because append returns None, and the or continues on to search for a truth-y value, which x is (it's a list with at least what was appended to it).
Why do I even need this?
Say you have a list and you want to insert some of it's members to a new list, and update the references accordingly:
So you have the list all:
>>> all = [[], [], [], []]
Some of it is inserted and updated to a new list x:
>>> x = [i.append('x') or i for i in all[:2]]
>>> x
[['x'], ['x']]
Some of all is also inserted and updated to a list y:
>>> y = [i.append('y') or i for i in all[1:3]]
all is updated:
>>> all
[['x'], ['x', 'y'], ['y'], []]
But x is also updated:
>>> x
[['x'], ['x', 'y']]
And y is generated as expected:
>>> y
[['x', 'y'], ['y']]
Overall, for simple tasks, I'd recommend using a for loop updating explicitly. This is what's considered pythonic.
Technically speaking, if you had access to the list class, you could make this a function:
def more_functional_append(self, x):
self.append(x)
return self
functional programming is based on every statement doing essentially one thing, and not having side effects (so, not mutating and returning). append is not very functional since it mutates a list (pure functional programming has only immutable objects) and does not return a result to pass to other actions (functions). Using functional programming concepts you can create great big one-liners no one can read, also known as "job security" or "bad code".
For the first case, the reason it returns [None, None, None] is because the list.append function returns None, and that's what it stores in the list.
In the second case, it's because the list is mutable, and each time you append the value, the original list is modified.
What you need is a non-in-place append operator, such as +. i.e. [x + ['a'] for x in a].
You can use list addition within a list comprehension, like the following:
a = [x + ['a'] for x in a]
This gives the desired result for a. One could make it more efficient in this case by assigning ['a'] to a variable name before the loop, but it depends what you want to do.
(This is a combination of the answers by Mike Graham and sykora):
If you merely want to change the values in-place, try a regular for-loop, and not a list comprehension:
for sublist in a:
sublist.append('a')
If you want to leave a alone, and put the result in a1:
a1 = [sublist + ['a'] for sublist in a]
As they explained, append modifies the list, but returns None, while + leaves the list alone, but returns a new, appended list.
In the first value assignment of your list comprehension an Attribute Error, 'NoneType' object has no attribute 'append', helps explain why your list, a, will be loaded with None(s). To get my console to throw the error, I used x as a variable for the list comprehension and also as the iterator.
Traceback (most recent call last):
x = [x.append('a') for x in a]
AttributeError: 'NoneType' object has no attribute 'append'
Then, I reverted back to a for x and it threw the same error.
Traceback (most recent call last):
a = [x.append('a') for x in a]
AttributeError: 'NoneType' object has no attribute 'append'
leave the a = and use the side effect on a:
[x.append('a') for x in a]
print a

Categories