Assign multiple values of a list - python

I am curious to know if there is a "pythonic" way to assign the values in a list to elements? To be clearer, I am asking for something like this:
myList = [3, 5, 7, 2]
a, b, c, d = something(myList)
So that:
a = 3
b = 5
c = 7
d = 2
I am looking for any other, better option than doing this manually:
a = myList[0]
b = myList[1]
c = myList[2]
d = myList[3]

Simply type it out:
>>> a,b,c,d = [1,2,3,4]
>>> a
1
>>> b
2
>>> c
3
>>> d
4
Python employs assignment unpacking when you have an iterable being assigned to multiple variables like above.
In Python3.x this has been extended, as you can also unpack to a number of variables that is less than the length of the iterable using the star operator:
>>> a,b,*c = [1,2,3,4]
>>> a
1
>>> b
2
>>> c
[3, 4]

Totally agree with NDevox's answer
a,b,c,d = [1,2,3,4]
I think it is also worth to mention that if you only need part of the list e.g only the second and last element from the list, you could do
_, a, _, b = [1,2,3,4]

a, b, c, d = myList
is what you want.
Basically, the function returns a tuple, which is similar to a list - because it is an iterable.
This works with all iterables btw. And you need to know the length of the iterable when using it.

One trick is to use the walrus operator in Python 3.8 so that you still have the my_list variable. And make it a one-line operation.
>>> my_list = [a:=3, b:=5, c:=7, d:=2]
>>> a
3
>>> b
5
>>> c
7
>>> d
2
>>> my_list
[3, 5, 7, 2]
PS: Using camelCase (myList) is not pythonic too.
What's new in Python 3.8 : https://docs.python.org/3/whatsnew/3.8.html

You can also use a dictionary. This was if you have more elements in a list, you don't have to waste time hardcoding that.
import string
arr = [1,2,3,4,5,6,7,8,9,10]
var = {let:num for num,let in zip(arr,string.ascii_lowercase)}
Now we can access variables in this dictionary like so.
var['a']

Related

Unpack into the list append method in Python

I run into a problem when unpacking a tuple. I want the first value to be appended to a list and a second assigned to a variable. For example:
list = []
tuple = (1, 2)
list.append, variable = tuple
But this raises an exception since I am assigning to a bultin and not actually calling in. Is that possible in Python? Or even a simpler operation such as:
a, b = 5, 4
tuple = (1, 2)
a+, b = tuple
to yield a = 6, b = 2.
There's no brief syntax to allow this. However, here's a class that creates a wrapper around a list, so that assigning to an append attribute really calls the underlying list's append method. This could be useful if you have a lot of values to append to the list.
class Appender:
def __init__(self, lst):
self.lst = lst
# The rare write-only property
append = property(None, lambda self, v: self.lst.append(v))
values = []
value_appender = Appender(values)
value_appender.append, b = (1,2)
assert values == [1]
Perhaps simpler, a subclass of list with a similar property:
class Appendable(list):
take = property(None, lambda self, v: self.append(v))
values = Appendable()
values.take, b = (1, 2)
assert values == [1]
append is a method on the builtin list type. Python allows tuple unpacking into variables in one line as a convenience, but it won't decide to call the append method with part of your tuple as an argument. Just write your code on multiple lines, that will help make it easier to read too.
my_list = []
my_tuple = (1, 2)
a, b = my_tuple
my_list.append(a)
Technically yes you could do it in a single line, but I wouldn't.
l = []
a = (1,2)
l[:0], b = [[x] if c == 0 else x for c,x in enumerate(a)]
>>> l
[1]
>>> b
2
You can use the map function on the append method for the list.
>>> a = (6,7)
>>> b = [1,2,3,4,5]
>>> list(map(b.append, a))
[None, None]
>>> b
[1, 2, 3, 4, 5, 6, 7]
I am not really sure what the list() does in this statement but it seems to work.

Is there any Hashable list datatype? [duplicate]

In Java we have HashSet<Integer>, I need similar structure in Python to use contains like below:
A = [1, 2, 3]
S = set()
S.add(2)
for x in A:
if S.contains(x):
print "Example"
Could you please help?
Just use a set:
>>> l = set()
>>> l.add(1)
>>> l.add(2)
>>> 1 in l
True
>>> 34 in l
False
The same works for lists:
>>> ll = [1,2,3]
>>> 2 in ll
True
>>> 23 in ll
False
Edit:
Note #bholagabbar's comment below that the time complexity for in checks in lists and tuples is O(n) on average (see the python docs here), whereas for sets it is on average O(1) (worst case also O(n), but is very uncommon and might only happen if __hash__ is implemented poorly).
In Python, there is a built-in type, set.
The major difference from the hashmap in Java is that the Python set is not typed,
i.e., it is legal to have a set {'2', 2} in Python.
Out of the box, the class set does not have a contains() method implemented.
We typically use the Python keyword in to do what you want, i.e.,
A = [1, 2, 3]
S = set()
S.add(2)
for x in A:
if x in S:
print("Example")
If that does not work for you, you can invoke the special method __contains__(), which is NOT encouraged.
A = [1, 2, 3]
S = set()
S.add(2)
for x in A:
if S.__contains__(x):
print("Example")

Understanding *x ,= lst

I'm going through some old code trying to understand what it does, and I came across this odd statement:
*x ,= p
p is a list in this context. I've been trying to figure out what this statement does. As far as I can tell, it just sets x to the value of p. For example:
p = [1,2]
*x ,= p
print(x)
Just gives
[1, 2]
So is this any different than x = p? Any idea what this syntax is doing?
*x ,= p is basically an obfuscated version of x = list(p) using extended iterable unpacking. The comma after x is required to make the assignment target a tuple (it could also be a list though).
*x, = p is different from x = p because the former creates a copy of p (i.e. a new list) while the latter creates a reference to the original list. To illustrate:
>>> p = [1, 2]
>>> *x, = p
>>> x == p
True
>>> x is p
False
>>> x = p
>>> x == p
True
>>> x is p
True
It's a feature that was introduced in Python 3.0 (PEP 3132). In Python 2, you could do something like this:
>>> p = [1, 2, 3]
>>> q, r, s = p
>>> q
1
>>> r
2
>>> s
3
Python 3 extended this so that one variable could hold multiple values:
>>> p = [1, 2, 3]
>>> q, *r = p
>>> q
1
>>> r
[2, 3]
This, therefore, is what is being used here. Instead of two variables to hold three values, however, it is just one variable that takes each value in the list. This is different from x = p because x = p just means that x is another name for p. In this case, however, it is a new list that just happens to have the same values in it. (You may be interested in "Least Astonishment" and the Mutable Default Argument)
Two other common ways of producing this effect are:
>>> x = list(p)
and
>>> x = p[:]
Since Python 3.3, the list object actually has a method intended for copying:
x = p.copy()
The slice is actually a very similar concept. As nneonneo pointed out, however, that works only with objects such as lists and tuples that support slices. The method you mention, however, works with any iterable: dictionaries, sets, generators, etc.
You should always throw these to dis and see what it throws back at you; you'll see how *x, = p is actually different from x = p:
dis('*x, = p')
1 0 LOAD_NAME 0 (p)
2 UNPACK_EX 0
4 STORE_NAME 1 (x)
While, the simple assignment statement:
dis('x = p')
1 0 LOAD_NAME 0 (p)
2 STORE_NAME 1 (x)
(Stripping off unrelated None returns)
As you can see UNPACK_EX is the different op-code between these; it's documented as:
Implements assignment with a starred target: Unpacks an iterable in TOS (top of stack) into individual values, where the total number of values can be smaller than the number of items in the iterable: one of the new values will be a list of all leftover items.
Which is why, as Eugene noted, you get a new object that's referred to by the name x and not a reference to an already existing object (as is the case with x = p).
*x, does seem very odd (the extra comma there and all) but it is required here. The left hand side must either be a tuple or a list and, due to the quirkiness of creating a single element tuple in Python, you need to use a trailing ,:
i = 1, # one element tuple
If you like confusing people, you can always use the list version of this:
[*x] = p
which does exactly the same thing but doesn't have that extra comma hanging around there.
You can clearly understand it from below example
L = [1, 2, 3, 4]
while L:
temp, *L = L
print(temp, L)
what it does is, the front variable will get the first item every time and the remaining list will be given to L.
The output will look shown below.
1 [2, 3, 4]
2 [3, 4]
3 [4]
4 []
Also look at below example
x, *y, z = "python"
print(x,y,z)
In this both x,z will get each one letter from the string meaning first letter is assigned to x and the last letter will be assigned to z and the remaining string will be assigned to variable y.
p ['y', 't', 'h', 'o'] n
One more example,
a, b, *c = [0,1,2,3]
print(a,b,c)
0 1 [2,3]
Boundary case: If there is nothing remaining for star variable then it will get an empty list.
Example:
a,b=[1]
print(a,b)
1 []

Store reference to primitive type in Python?

Code:
>>> a = 1
>>> b = 2
>>> l = [a, b]
>>> l[1] = 4
>>> l
[1, 4]
>>> l[1]
4
>>> b
2
What I want to instead see happen is that when I set l[1] equal to 4, that the variable b is changed to 4.
I'm guessing that when dealing with primitives, they are copied by value, not by reference. Often I see people having problems with objects and needing to understand deep copies and such. I basically want the opposite. I want to be able to store a reference to the primitive in the list, then be able to assign new values to that variable either by using its actual variable name b or its reference in the list l[1].
Is this possible?
There are no 'primitives' in Python. Everything is an object, even numbers. Numbers in Python are immutable objects. So, to have a reference to a number such that 'changes' to the 'number' are 'seen' through multiple references, the reference must be through e.g. a single element list or an object with one property.
(This works because lists and objects are mutable and a change to what number they hold is seen through all references to it)
e.g.
>>> a = [1]
>>> b = a
>>> a
[1]
>>> b
[1]
>>> a[0] = 2
>>> a
[2]
>>> b
[2]
You can't really do that in Python, but you can come close by making the variables a and b refer to mutable container objects instead of immutable numbers:
>>> a = [1]
>>> b = [2]
>>> lst = [a, b]
>>> lst
[[1], [2]]
>>> lst[1][0] = 4 # changes contents of second mutable container in lst
>>> lst
[[1], [4]]
>>> a
[1]
>>> b
[4]
I don't think this is possible:
>>> lst = [1, 2]
>>> a = lst[1] # value is copied, not the reference
>>> a
2
>>> lst[1] = 3
>>> lst
[1, 3] # list is changed
>>> a # value is not changed
2
a refers to the original value of lst[1], but does not directly refer to it.
Think of l[0] as a name referring to an object a, and a as a name that referring to an integer.
Integers are immutable, you can make names refer to different integers, but integers themselves can't be changed.
There were a relevant discussion earlier:
Storing elements of one list, in another list - by reference - in Python?
According to #mgilson, when doing l[1] = 4, it simply replaces the reference, rather than trying to mutate the object. Nevertheless, objects of type int are immutable anyway.

An elegant and fast way to consecutively iterate over two or more containers in Python?

I have three collection.deques and what I need to do is to iterate over each of them and perform the same action:
for obj in deque1:
some_action(obj)
for obj in deque2:
some_action(obj)
for obj in deque3:
some_action(obj)
I'm looking for some function XXX which would ideally allow me to write:
for obj in XXX(deque1, deque2, deque3):
some_action(obj)
The important thing here is that XXX have to be efficient enough - without making copy or silently using range(), etc. I was expecting to find it in built-in functions, but I found nothing similar to it so far.
Is there such thing already in Python or I have to write a function for that by myself?
Depending on what order you want to process the items:
import itertools
for items in itertools.izip(deque1, deque2, deque3):
for item in items:
some_action(item)
for item in itertools.chain(deque1, deque2, deque3):
some_action(item)
I'd recommend doing this to avoid hard-coding the actual deques or number of deques:
deques = [deque1, deque2, deque3]
for item in itertools.chain(*deques):
some_action(item)
To demonstrate the difference in order of the above methods:
>>> a = range(5)
>>> b = range(5)
>>> c = range(5)
>>> d = [a, b, c]
>>>
>>> for items in itertools.izip(*d):
... for item in items:
... print item,
...
0 0 0 1 1 1 2 2 2 3 3 3 4 4 4
>>>
>>> for item in itertools.chain(*d):
... print item,
...
0 1 2 3 4 0 1 2 3 4 0 1 2 3 4
>>>
The answer is in itertools
itertools.chain(*iterables)
Make an iterator that returns elements from the first iterable until
it is exhausted, then proceeds to the
next iterable, until all of the
iterables are exhausted. Used for
treating consecutive sequences as a
single sequence. Equivalent to:
def chain(*iterables):
# chain('ABC', 'DEF') --> A B C D E F
for it in iterables:
for element in it:
yield element
Call me crazy, but why is using itertools thought to be necessary? What's wrong with:
def perform_func_on_each_object_in_each_of_multiple_containers(func, containers):
for container in containers:
for obj in container:
func(obj)
perform_func_on_each_object_in_each_of_multiple_containers(some_action, (deque1, deque2, deque3)
Even crazier: you probably are going to use this once. Why not just do:
for d in (deque1, deque2, deque3):
for obj in d:
some_action(obj)
What's going on there is immediately obvious without having to look at the code/docs for the long-name function or having to look up the docs for itertools.something()
Use itertools.chain(deque1, deque2, deque3)
How about zip?
for obj in zip(deque1, deque2, deque3):
for sub_obj in obj:
some_action(sub_obj)
Accepts a bunch of iterables, and yields the contents for each of them in sequence.
def XXX(*lists):
for aList in lists:
for item in aList:
yield item
l1 = [1, 2, 3, 4]
l2 = ['a', 'b', 'c']
l3 = [1.0, 1.1, 1.2]
for item in XXX(l1, l2, l3):
print item
1
2
3
4
a
b
c
1.0
1.1
1.2
It looks like you want itertools.chain:
"Make an iterator that returns elements from the first iterable until it is exhausted, then proceeds to the next iterable, until all of the iterables are exhausted. Used for treating consecutive sequences as a single sequence."
If I understand your question correctly, then you can use map with the first argument set to None, and all the other arguments as your lists to iterate over.
E.g (from an iPython prompt, but you get the idea):
In [85]: p = [1,2,3,4]
In [86]: q = ['a','b','c','d']
In [87]: f = ['Hi', 'there', 'world', '.']
In [88]: for i,j,k in map(None, p,q,f):
....: print i,j,k
....:
....:
1 a Hi
2 b there
3 c world
4 d .
I would simply do this :
for obj in deque1 + deque2 + deque3:
some_action(obj)
>>> a = [[],[],[]]
>>> b = [[],[],[]]
>>> for c in [*a,*b]:
c.append("derp")
>>> a
[['derp'], ['derp'], ['derp']]
>>> b
[['derp'], ['derp'], ['derp']]
>>>

Categories