Python [0]*n syntax works only on immutable object? - python

a = [[0]*3] * 4
print(a[0] is a[1]) # True
when I initialize a two dimensional array that way, things went wrong.
Took me a bit time to find this unexpected behavior. So is this syntax only work on immutable object?

It "worked" in your example too, in its way. This is just how the implementation of list.__mul__ interprets what you want. The list can't construct new objects, it doesn't know how to create new objects from whatever objects it happens to contain. It expands itself with new references to those objects.
You get the same behavior with immutable integers
>>> x = [0] * 3
>>> x[0] is x[1]
True
You can get the two dimensional array with
>>> a = [[0]*3 for _ in range(4)]
>>> a[0] is a[1]
False
The reason why [0] * 3 does what you want is that it is creating list that contains 3 references to the same immutable 0.

What went"wrong"?
It does make a two-dimensional list. (I don't know if that's a thing but I mean to say it makes the list in a form where you can convert it to a 2-D array).
To convert it to a proper array, you will have to use
import numpy as np
a = [[0]*3] * 4
a = np.array(a)
You can make this immutable by converting it to a tuple.

Related

Why does b+=(4,) work and b = b + (4,) doesn't work when b is a list?

If we take b = [1,2,3] and if we try doing: b+=(4,)
It returns b = [1,2,3,4], but if we try doing b = b + (4,) it doesn't work.
b = [1,2,3]
b+=(4,) # Prints out b = [1,2,3,4]
b = b + (4,) # Gives an error saying you can't add tuples and lists
I expected b+=(4,) to fail as you can't add a list and a tuple, but it worked. So I tried b = b + (4,) expecting to get the same result, but it didn't work.
The problem with "why" questions is that usually they can mean multiple different things. I will try to answer each one I think you might have in mind.
"Why is it possible for it to work differently?" which is answered by e.g. this. Basically, += tries to use different methods of the object: __iadd__ (which is only checked on the left-hand side), vs __add__ and __radd__ ("reverse add", checked on the right-hand side if the left-hand side doesn't have __add__) for +.
"What exactly does each version do?" In short, the list.__iadd__ method does the same thing as list.extend (but because of the language design, there is still an assignment back).
This also means for example that
>>> a = [1,2,3]
>>> b = a
>>> a += [4] # uses the .extend logic, so it is still the same object
>>> b # therefore a and b are still the same list, and b has the `4` added
[1, 2, 3, 4]
>>> b = b + [5] # makes a new list and assigns back to b
>>> a # so now a is a separate list and does not have the `5`
[1, 2, 3, 4]
+, of course, creates a new object, but explicitly requires another list instead of trying to pull elements out of a different sequence.
"Why is it useful for += to do this? It's more efficient; the extend method doesn't have to create a new object. Of course, this has some surprising effects sometimes (like above), and generally Python is not really about efficiency, but these decisions were made a long time ago.
"What is the reason not to allow adding lists and tuples with +?" See here (thanks, #splash58); one idea is that (tuple + list) should produce the same type as (list + tuple), and it's not clear which type the result should be. += doesn't have this problem, because a += b obviously should not change the type of a.
They are not equivalent:
b += (4,)
is shorthand for:
b.extend((4,))
while + concatenates lists, so by:
b = b + (4,)
you're trying to concatenate a tuple to a list
When you do this:
b += (4,)
is converted to this:
b.__iadd__((4,))
Under the hood it calls b.extend((4,)), extend accepts an iterator and this why this also work:
b = [1,2,3]
b += range(2) # prints [1, 2, 3, 0, 1]
but when you do this:
b = b + (4,)
is converted to this:
b = b.__add__((4,))
accept only list object.
From the official docs, for mutable sequence types both:
s += t
s.extend(t)
are defined as:
extends s with the contents of t
Which is different than being defined as:
s = s + t # not equivalent in Python!
This also means any sequence type will work for t, including a tuple like in your example.
But it also works for ranges and generators! For instance, you can also do:
s += range(3)
The "augmented" assignment operators like += were introduced in Python 2.0, which was released in October 2000. The design and rationale are described in PEP 203. One of the declared goals of these operators was the support of in-place operations. Writing
a = [1, 2, 3]
a += [4, 5, 6]
is supposed to update the list a in place. This matters if there are other references to the list a, e.g. when a was received as a function argument.
However, the operation can't always happen in place, since many Python types, including integers and strings, are immutable, so e.g. i += 1 for an integer i can't possibly operate in place.
In summary, augmented assignment operators were supposed to work in place when possible, and create a new object otherwise. To facilitate these design goals, the expression x += y was specified to behave as follows:
If x.__iadd__ is defined, x.__iadd__(y) is evaluated.
Otherwise, if x.__add__ is implemented x.__add__(y) is evaluated.
Otherwise, if y.__radd__ is implemented y.__radd__(x) is evaluated.
Otherwise raise an error.
The first result obtained by this process will be assigned back to x (unless that result is the NotImplemented singleton, in which case the lookup continues with the next step).
This process allows types that support in-place modification to implement __iadd__(). Types that don't support in-place modification don't need to add any new magic methods, since Python will automatically fall back to essentially x = x + y.
So let's finally come to your actual question – why you can add a tuple to a list with an augmented assignment operator. From memory, the history of this was roughly like this: The list.__iadd__() method was implemented to simply call the already existing list.extend() method in Python 2.0. When iterators were introduced in Python 2.1, the list.extend() method was updated to accept arbitrary iterators. The end result of these changes was that my_list += my_tuple worked starting from Python 2.1. The list.__add__() method, however, was never supposed to support arbitrary iterators as the right-hand argument – this was considered inappropriate for a strongly typed language.
I personally think the implementation of augmented operators ended up being a bit too complex in Python. It has many surprising side effects, e.g. this code:
t = ([42], [43])
t[0] += [44]
The second line raises TypeError: 'tuple' object does not support item assignment, but the operation is successfully performed anyway – t will be ([42, 44], [43]) after executing the line that raises the error.
Most people would expect X += Y to be equivalent to X = X + Y. Indeed, the Python Pocket Reference (4th ed) by Mark Lutz says on page 57 "The following two formats are roughly equivalent: X = X + Y , X += Y". However, the people who specified Python did not make them equivalent. Possibly that was a mistake which will result in hours of debugging time by frustrated programmers for as long as Python remains in use, but it's now just the way Python is. If X is a mutable sequence type, X += Y is equivalent to X.extend( Y ) and not to X = X + Y.
As it's explained here, if array doesn't implement __iadd__ method, the b+=(4,) would be just a shorthanded of b = b + (4,) but obviously it's not, so array does implement __iadd__ method. Apparently the implementation of __iadd__ method is something like this:
def __iadd__(self, x):
self.extend(x)
However we know that the above code is not the actual implementation of __iadd__ method but we can assume and accept that there's something like extend method, which accepts tupple inputs.

Comma index in python list bracket

example is below. What is this? A comma is in the python list-bracket. How can I understand this?
id(x[i,j]) == id(x[i][j]) shows True.
So, x[i,j] is always same at x[i][j]?
or, x[i,j] is actually same as x[(i,j)]? index is tuple.
import numpy as np
x = np.random.random((64,3,32,10))
for i in range(x.shape[0]):
for j in range(x.shape[1]):
print(x[i,j])
======
I guess
this is numpy's special implementation. Numpy overwrites "_getitem__" function for using tuple index.
I tried it for
1) basic python
2) overwrite "_getitem__ " and
3) basic numpy.
1) shows error. "TypeError: list indices must be integers or slices, not tuple"
2) has....no problem if I implemented right.
3) just works well.
So, I guess numpy developers did implemented their "getitem" function for tuple, and it works same arr[x,y,z] == arr[x][y][z].
While numpy behaves the same way with the two, they are technically different.
You can see the difference by implementing __getitem__ yourself:
class GetitemTest(object):
def __getitem__(self, item):
print("getting: %r" %(item, ))
return self
Then:
>>> x = GetitemTest()
>>> x[1,2]
getting: (1, 2)
<__main__.GetitemTest object at 0x10bb6d810>
>>> x[1][2]
getting: 1
getting: 2
<__main__.GetitemTest object at 0x10bb6d810>
Notice that x[1,2] only calls __getitem__ once, but x[1][2] calls it twice.
This is Numpy special ability.
Numpy official docs
"Unlike lists and tuples, numpy arrays support multidimensional indexing for multidimensional arrays. That means that it is not necessary to separate each dimension’s index into its own set of square brackets."
>>> x.shape = (2,5) # now x is 2-dimensional
>>> x[1,3]
8
>>> x[1,-1]
9

TypeError: unsupported operand type(s) for ** or pow(): 'list' and 'int'

I'm trying to take the values from the previous function and use in another function. This is my first programming class and language, and i'm totally lost.
I figured out how to take the variables from astlist and put them into the function distance, but now Python is telling me I can't use these variables in an equation because they're in a list now? Is that what it's saying?
I'm also just printing the lists to see if they are running. These are two of my functions, and the functions are both defined in my main function.
I'm taking these lists and eventually putting them into files, but I need to figure out why the equation isn't working first. Thanks!
def readast():
astlist = []
for j in range(15):
list1 = []
for i in range(3):
x = random.randint(1,1000)
y = random.randint(1,1000)
z = random.randint(1,1000)
list1.append([x,y,z])
astlist.append(list1)
print(astlist)
return astlist
def distance(astlist):
distlist = []
for row in range(len(astlist)):
x, y, z = astlist[row]
x1 = x**2
y2 = y**2
z2 = z**2
equation = math.sqrt(x+y+z)
distlist.append(equation)
print(distlist)
return distlist
The variable astlist is a list. You're adding list1 to it several times which is also a list. But you're also adding a list to list1 each time: list1.append([x,y,z]). So ultimately astlist is a list containing multiple lists which each contain a list with three integers.
So when you write x,y,z=astlist[row] the variables x, y and z are actually lists, not integers. This means you're trying to compute x**2 but x is a list, not a number. This is why Python is giving you an error message as ** doesn't support raising a list to a power.
I'm not sure what you're trying to accomplish with all these lists but you should change the code so that you're only trying to raise numbers to the power of two and not lists.
There are a few problems here:
Firstly the loop at the top of readast() sets list1 to [] 15 times - I'm not sure what you're trying to do here. If you are trying to generate 15 sets of x,y,z coordinates then it is the second range - in your example the range(3)
- that you need to change.
Then you keep adding lists of [x,y,z] to (the same) list1, then adding the whole of list1 to astlist. However, Python actually stores a pointer to the list rather than a copy so when you add items to list1, it adds items to list1 whereever list1 is included in another list:
In this example the random numbers are replaced with sequential numbers for clarity (the first random number is 1, the second 2 and so on):
After first cycle of loop:
list1: [[1,2,3]]
astlist: [[[1,2,3]]]
After second cycle of loop:
list1: [[1,2,3],[4,5,6]]
astlist: [[[1,2,3],[4,5,6]],[[1,2,3],[4,5,6]]]
and so on
As you can see, list1 is now a list of lists, and astlist is now a list of duplicates of list1 (a list of lists of lists)
list1 is probably redundant and you probably want just
astlist.append([x,y,z])
in the first bit.
In the second function, you use
for row in range(len(astlist)):
x,y,z=astlist[row]
...
but actually the following would be better:
for row in astlist:
x,y,z=row
...
or even:
for x,y,z in astlist:
...
as for loops in Python iterate over members of a sequence (or other iterable value) rather being just a simple counter. What you are doing with the range(len(astlist)) construct is actually generating a list [1,2,3...] and iterating over that.
If you particularly need a numerical index then you can use the enumerate function which returns a series of (index,value) pairs that you can iterate over thus:
for i,value in enumerate(['apple','banana','cherry']):
print 'value {} is {}'.format(i,value)
value 0 is apple
value 1 is ball
value 2 is cherry
Hope this helps
I will refer to the specific type error (TypeError: unsupported operand type(s) for ** or pow(): 'list' and 'int'), for some reason arrays are iterable objects for operations like addition: sum(array), but, power: array**2, pow(array,2). You can solve this with some extra steps as follow:
x1 = [j**2 for j in x]
also I recommend to use sum function
sum(x,y,z)
remember all this is to avoid the error message that you were referring to
that way you apply the power of 2 to each element in the array, getting a new array and avoiding the error message that you were asking help for. It seems to me that you are looking to get a normalization of your data using norm L2, if that is true, well, I think you are missing half of it.

Printing out 2d list in python 2.7

I'm sorry for such a stupid question, I'm not used to object-oriented programming (I come from a C background). To put it bluntly, I'm very clueless on classes. First of all, is this how you initialize a list attribute and set each element to None?
class World:
def __init__(self):
self.arr = [[None for x in range(20)] for y in range(20)]
Also, I wanted to check if each value was, in fact, set to none but I don't know how to print it out.
Since you accepted the answer I can't delete it, so I will try to amend it.
This is the wrong way:
mylist = [[None] * 2] * 2 # Definitely not what you want
The above is bad because if you do
mylist[0].append(1)
The output will be
[[None, None, 1], [None, None, 1]]
The reason is that we didn't create a list of 2 lists, but a list with 2
references to the same list.
The correct way would either be what you already have or:
mylist = [[None] * 20 for i in range(20)]
I am not sure what you mean by "wanted to check if it was set to None".
Something like this for example?
w = World() # make a world instance
print w.arr # This should print your 2d list

Python array combining

I'm trying to port some Python code to Java. I'm not familiar with Python and have never seen this in any language before:
return [c,] + s
What exactly does this line mean? Specifically the [c,] part. Is it combining two arrays or something? s is an array of integers and c is an integer. The full function is below (from Wikipedia: http://en.wikipedia.org/wiki/Ring_signature )
def sign(self,m,z):
self.permut(m)
s,u = [None]*self.n,random.randint(0,self.q)
c = v = self.E(u)
for i in range(z+1,self.n)+range(z):
s[i] = random.randint(0,self.q)
v = self.E(v^self.g(s[i],self.k[i].e,self.k[i].n))
if (i+1)%self.n == 0: c = v
s[z] = self.g(v^u,self.k[z].d,self.k[z].n)
return [c,] + s
Thanks so much!
The comma is redundant. It's just creating a one-element list:
>>> [1,]
[1]
>>> [1] == [1,]
True
The practice comes from creating tuples in Python; a one-element tuple requires a comma:
>>> (1)
1
>>> (1,)
(1,)
>>> (1) == (1,)
False
The [c,] + s statement creates a new list with the value of c as the first element.
[c,] is exactly the same as [c], i.e. a single-item list.
(See this answer for why this syntax is needed)
For a list, the extra comma is redundant and can be ignored. The only time it makes a difference if it had been a tuple instead of a list so
[c,] and [c] are the same but,
(c,) and (c) are different. The former being a tuple and later just a parenthesis around an expression
to answer both your questions, the line concatenates two lists, the first of the two is a one-element list since the comma is just ignored by python
I believe you are correct, it is combining two "arrays" (lists in python). If I'm not mistaken, the trailing comma is unnecessary in this instance.
x = [1,2,3]
y = [1] + x
#is equivalent to
x = [1,2,3]
y = [1,] + x
The reason Python allows the use of trailing commas in lists has to do with another data type called a tuple and ease of use with multi-line list declaration in code.
Why does Python allow a trailing comma in list?

Categories

Resources