Usage of "__add__" method in Tuple class - python

python noob here and playing around with the limitations of Tuples and Lists.
I don't have a problem, just a general query about usage of the __methodname__ methods in the Tuple class and/or in general.
I am aware that you cannot modify Tuples and in order to do so, you have to convert it to a list, modify said list, then convert back to Tuple but I had a play around with the __add__ method and found that it works. What are issues and limitations with using this to create new Tuples with modifications to an existing one?
CODE:
myTuple = ('item1', 2, 'item3', ['list1', 'list2'])
tupleModification = myTuple.__add__(('newTupleItem1','newTupleItem2'))
This outputs the following:
('item1', 2, 'item3', ['list1', 'list2'], 'newTupleItem1', 'newTupleItem2')
which is correct but i'm wondering if i'm playing with fire because I haven't seen this solution posted anywhere in relation to modifying Tuples.
EDIT: I am aware that you cannot modify existing Tuples and that this will create a new instance of one. I think I may have confused people with my naming conventions.

__add__ is the method which is called when you do:
myTuple + ("newTupleItem1", "newTupleItem2")
So this does not modify myTuple but creates a new tuple whose content is the content of myTuple concatenated with ("newTupleItem1", "newTupleItem2").
You can print myTuple to see that it has not been modified:
>>> myTuple
('item1', 2, 'item3', ['list1', 'list2'])
And you can check that myTuple and tupleModification are not the same object:
>>> myTuple is tupleModification
False

You cannot modify tuples, that's right. However, you can concatenate two existing tuples into a new tuple. This is done with the + operator, which in turn calls the __add__ method. The resulting tuple will not be a "modification" of any of the original ones, but a new distinct tuple. This is what the code you posted does. More concisely, you can just do:
myTuple = ('item1', 2, 'item3', ['list1', 'list2'])
tupleModification = myTuple + ('newTupleItem1','newTupleItem2')
print(tupleModification)
# ('item1', 2, 'item3', ['list1', 'list2'], 'newTupleItem1', 'newTupleItem2')
EDIT: Just as a clarification, you cannot "edit" a tuple anyhow, that is, add or remove elements from it, or change its contents. However, if your tuple contains a mutable object, such as a list, then that inner object can be modified:
myTuple = (1, [2, 3])
myTuple[1].append(4)
print(myTuple)
# (1, [2, 3, 4])

I think fundamentally you're confused about the difference between creating a new object with modifications (__add__ in this case) and modifying an existing object (extend for example).
__add__
As other answers already mentioned, the __add__ method implements the + operator, and it returns a new object. Tuples and lists each have one. For example:
>>> tuple_0 = (1,)
>>> tuple_1 = tuple_0.__add__((2,))
>>> tuple_1 is tuple_0
False
>>>
>>> list_0 = [1]
>>> list_1 = list_0.__add__([2])
>>> list_1 is list_0
False
extend
Lists, which are mutable, have an extend method, which modifies the existing object and returns None. Tuples, which are immutable, don't. For example:
>>> list_2 = [4, 5, 6]
>>> id_save = id(list_2)
>>> list_2.extend([7])
>>> id(list_2) == id_save
True
>>>
>>> tuple_2 = (4, 5, 6)
>>> tuple_2.extend([7])
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'tuple' object has no attribute 'extend'
Lists have other methods which change the existing object, like append, sort, pop, etc, but extend is the most similar to __add__.

There is a difference between 1) modifying an existing tuple (in-place), and 2) leaving the original alone, but creating a new modified copy. These two different paradigms can be seen throughout computer programming, not just python tuples. For example, consider increment a number by one. You could modify the original, or you could leave the original alone, create a copy and then modify the copy.
MODIFYING IN-PLACE
BEFORE:
x == 5
AFTER:
x == 6
CREATING A MODIFIED COPY
BEFORE:
x == 5
AFTER:
x == 5 (unchanged)
y == 6
tuple.__add__ concatenates tuples. For example, (x, y) + (a, b, c) returns (x, y, a, b, c)
tuple.__add__ does not modify the original tuples. It leaves the original tuples alone, and creates a new tuple which is the concatenation of the original two. This is contrast to something like list.append or list.extend which modifies the original list instead of returning a modified copy.
Tuple methods generally do the following:
copy the original
modify the copy in some way
leave the original alone.

Related

What's the difference between list.append(a) vs. list = list + [i] (for the purposes of recursion)? [duplicate]

Why do these two operations (append() resp. +) give different results?
>>> c = [1, 2, 3]
>>> c
[1, 2, 3]
>>> c += c
>>> c
[1, 2, 3, 1, 2, 3]
>>> c = [1, 2, 3]
>>> c.append(c)
>>> c
[1, 2, 3, [...]]
>>>
In the last case there's actually an infinite recursion. c[-1] and c are the same. Why is it different with the + operation?
To explain "why":
The + operation adds the array elements to the original array. The array.append operation inserts the array (or any object) into the end of the original array, which results in a reference to self in that spot (hence the infinite recursion in your case with lists, though with arrays, you'd receive a type error).
The difference here is that the + operation acts specific when you add an array (it's overloaded like others, see this chapter on sequences) by concatenating the element. The append-method however does literally what you ask: append the object on the right-hand side that you give it (the array or any other object), instead of taking its elements.
An alternative
Use extend() if you want to use a function that acts similar to the + operator (as others have shown here as well). It's not wise to do the opposite: to try to mimic append with the + operator for lists (see my earlier link on why). More on lists below:
Lists
[edit] Several commenters have suggested that the question is about lists and not about arrays. The question has changed, though I should've included this earlier.
Most of the above about arrays also applies to lists:
The + operator concatenates two lists together. The operator will return a new list object.
List.append does not append one list with another, but appends a single object (which here is a list) at the end of your current list. Adding c to itself, therefore, leads to infinite recursion.
As with arrays, you can use List.extend to add extend a list with another list (or iterable). This will change your current list in situ, as opposed to +, which returns a new list.
Little history
For fun, a little history: the birth of the array module in Python in February 1993. it might surprise you, but arrays were added way after sequences and lists came into existence.
The concatenation operator + is a binary infix operator which, when applied to lists, returns a new list containing all the elements of each of its two operands. The list.append() method is a mutator on list which appends its single object argument (in your specific example the list c) to the subject list. In your example this results in c appending a reference to itself (hence the infinite recursion).
An alternative to '+' concatenation
The list.extend() method is also a mutator method which concatenates its sequence argument with the subject list. Specifically, it appends each of the elements of sequence in iteration order.
An aside
Being an operator, + returns the result of the expression as a new value. Being a non-chaining mutator method, list.extend() modifies the subject list in-place and returns nothing.
Arrays
I've added this due to the potential confusion which the Abel's answer above may cause by mixing the discussion of lists, sequences and arrays.
Arrays were added to Python after sequences and lists, as a more efficient way of storing arrays of integral data types. Do not confuse arrays with lists. They are not the same.
From the array docs:
Arrays are sequence types and behave very much like lists, except that the type of objects stored in them is constrained. The type is specified at object creation time by using a type code, which is a single character.
append is appending an element to a list. if you want to extend the list with the new list you need to use extend.
>>> c = [1, 2, 3]
>>> c.extend(c)
>>> c
[1, 2, 3, 1, 2, 3]
Python lists are heterogeneous that is the elements in the same list can be any type of object. The expression: c.append(c) appends the object c what ever it may be to the list. In the case it makes the list itself a member of the list.
The expression c += c adds two lists together and assigns the result to the variable c. The overloaded + operator is defined on lists to create a new list whose contents are the elements in the first list and the elements in the second list.
So these are really just different expressions used to do different things by design.
The method you're looking for is extend(). From the Python documentation:
list.append(x)
Add an item to the end of the list; equivalent to a[len(a):] = [x].
list.extend(L)
Extend the list by appending all the items in the given list; equivalent to a[len(a):] = L.
list.insert(i, x)
Insert an item at a given position. The first argument is the index of the element before which to insert, so a.insert(0, x) inserts at the front of the list, and a.insert(len(a), x) is equivalent to a.append(x).
you should use extend()
>>> c=[1,2,3]
>>> c.extend(c)
>>> c
[1, 2, 3, 1, 2, 3]
other info: append vs. extend
See the documentation:
list.append(x)
Add an item to the end of the list; equivalent to a[len(a):] = [x].
list.extend(L)
- Extend the list by appending all the items in the given list;
equivalent to a[len(a):] = L.
c.append(c) "appends" c to itself as an element. Since a list is a reference type, this creates a recursive data structure.
c += c is equivalent to extend(c), which appends the elements of c to c.

Tuples in For Loops - Python [duplicate]

This question already has answers here:
How to change values in a tuple?
(17 answers)
Closed 1 year ago.
I want to subtract 1 from every digit in a tuple using for loop in Python.
I don't understand why my code is returning the original tuple instead of the desired one. Here is the code:
tuple = (3, 2)
for i in tuple:
i -= 1
print(tuple)
Any idea ?
Tuples are immutable objects, that is, you cannot change the elements of the tuple once it is created, if you do want to change them it would be better to use a list.
a = [3, 2]
for i in a:
i -= 1
print(a)
Which gives an output:
[3, 2]
Why didn't it work, even though lists are mutable? And also, why doesn't your original code produce some kind of error, if tuples are really immutable? Well, this becomes more obvious if you write your for each style loop as a simple for:
for index in range(len(a)):
i = a[index]
i -= 1
print(a)
This code is just a more verbose version of your original example, but now the problem is clear - the code never actually changes the elements of the original list. Instead, each iteration of the for loop creates a new variable, i, which takes its value from the correspondinglist element. i -= 1 changes i, not a[index].
Incidentally, that is why there is no error in your original code, even though tuples are supposed to be immutable - the code simply doesn't try to mutate the original tuple at all.
A working example with a list would be something like:
a = [3, 2]
for index in range(len(a)):
a[index] -= 1
print(a)
which can be made more concise in the end by writing:
a = [3, 2]
a = [i - 1 for i in a]
print(a)
As an aside, if you really must take tuples as input and produce tuples as output, you can convert between tuples and lists using the list and tuple functions. This is also why you shouldn't use tuple as your variable name! Doing so will mean that you can't use that tuple function in the same scope as your tuple variable, making things very difficult and potentially confusing for you.
tuple is immutable
my_tuple = (1,2,3)
my_tuple [0] = 3
Traceback (most recent call last):
File "<input>", line 1, in <module>
TypeError: 'tuple' object does not support item assignment
A couple of things going on. A tuple is immutable and you're assigning the value (3, 2) to the type tuple .
tup = (3, 2)
for i in tup:
i -= 1
print(tup)
Outputs:
(3, 2)
However you could do a list comprehension and cast it to tuple to get the expected result.
tup = (3,2)
tup = tuple([i - 1 for i in tup])
print(tup)
Outputs:
(2, 1)
Tuple is immutable.
try this, i think it will help you.
my_tuple = (3, 2)
new_list = []
for i in my_tuple:
i -= 1
new_list.append(i)
new_tuple = tuple(new_list)
print(new_tuple)
A brief introduction to python data structures should help you out here. Here's the gist
tuples are immutable i.e once created you cannot change the contents, you'll need to create a new one - you cannot modify the contents inplace similar to how strings work in python.
list makes sense but in your case it will still not work because you need to update the contents of the list when you do this:
tuple = (3, 2)
for i in tuple:
i -= 1
print(tuple)
you are basically extracting the contents from the tuple and modifying the value but not storing it anywhere. Even if you use a list like this:
list = [3,2]
for i in list:
i -= 1
print(list)
this will still not work as you are modifying a list item (a primitive type - int, string,etc.) which creates a new item but as it's not being stored it gets lost, when you print the list - it remains the same.
What we can do is modify the list as its mutable at runtime and update the contents like this:
list = [3, 2]
for idx, item in enumerate(list):
list[idx] = item - 1
print(list)
I think tuple is a bad variable name for a tuple because it shadows the tuple of Python
Issue is the values assigned to i are integers and integers themselves are not mutable. So even if i changes, the value in tuple does not. In other words, even though i points to the same object as the integer in tuple, when you do in place subtraction, a new integer is formed and i now points to that, different than what tuple has.
However, if you were to put a mutable object and do similar things:
tup = ([1, 2, 3], [4, -12])
for i in tup:
i[1] = 999
print(tup)
You get
([1, 999, 3], [4, 999])

Does tuple() copy the elements of the argument?

In python, does the built-in function tuple([iterable]) create a tuple object and fill it with copies of the elements of "iterable", or does it create a tuple containing references to the already existing objects of "iterable"?
tuple will iterate the sequence and copy the values. The underlying sequence will be not stored to actually keep the values, but the tuple representation will replace it. So yes, the conversion to a tuple is actual work and not just some nesting of another type.
You can see this happening when converting a generator:
>>> def gen ():
for i in range(5):
print(i)
yield i
>>> g = gen()
>>> g
<generator object gen at 0x00000000030A9B88>
>>> tuple(g)
0
1
2
3
4
(0, 1, 2, 3, 4)
As you can see, the generator is immediately iterated, making the values generate. Afterwards, the tuple is self-contained, and no reference to the original source is kept. For reference, list() behaves in exactly the same way but creates a list instead.
The behaviour that 275365 pointed out (in the now deleted answer) is the standard copying behaviour of Python values. Because everything in Python is an object, you are essentially only working with references. So when references are copied, the underlying object is not copied. The important bit is that non-mutable objects will be recreated whenever their value changes which will not update all previously existing references but just the one reference you are currently changing. That’s why it works like this:
>>> source = [[1], [2], [3]]
>>> tpl = tuple(source)
>>> tpl
([1], [2], [3])
>>> tpl[0].append(4)
>>> tpl
([1, 4], [2], [3])
>>> source
[[1, 4], [2], [3]]
tpl still contains a reference to the original objects within the source list. As those are lists, they are mutable. Changing a mutable list anywhere will not invalidate the references that exist to that list, so the change will appear in both source and tpl. The actual source list however is only stored in source, and tpl has no reference to it:
>>> source.append(5)
>>> source
[[1, 4], [2], [3], 5]
>>> tpl
([1, 4], [2], [3])
Yes. I think answer to both of your questions are a "yes". When you create a new tuple from an existing iterable, it will simply copy each item and add it to the new tuple object you are creating. Since variables in python are actually names referencing objects, the new tuple you are creating will actually hold references to the same objects as the iterable.
I think this question on variable passing will be helpful.
tuple([iterables]) will create a tuple object with the reference of the iterable. But, if the iterable is a tuple then it will return the same object else it will create a new tuple object initialized from the iterable items.
>>> a = (1,2)
>>> b = tuple(a)
>>> a is b
True
>>> c = [1,2]
>>> d = tuple(c)
>>> c is d
False
>>> c[0] is d[0]
True
>>> c[1] is d[1]
True
>>> type(c), type(d)
(<type 'list'>, <type 'tuple'>)
>>>
It will not copy or deep-copy the elements:
a = [{"key": "value"}]
x = tuple(a)
print x #=> ({"key": "value"},)
a[0]["key"] = "fish"
print x #=> ({"key": "fish"},)

Why do copied dictionaries point to the same directory but lists don't?

Can someone tell me why when you copy dictionaries they both point to the same directory, so that a change to one effects the other, but this is not the case for lists?
I am interested in the logic behind why they would set up the dictionary one way, and lists another. It's confusing and if I know the reason behind it I will probably remember.
dict = {'Dog' : 'der Hund' , 'Cat' : 'die Katze' , 'Bird' : 'der Vogel'}
otherdict = dict
dict.clear()
print otherdict
Which results in otherdict = {}.So both dicts are pointing to the same directory. But this isn't the case for lists.
list = ['one' , 'two' , 'three']
newlist = list
list = list + ['four']
print newlist
newlist still holds on to the old list. So they are not pointing to the same directory. I am wanting to know the rationale behind the reasons why they are different?
Some code with similar intent to yours will show that changes to one list do affect other references.
>>> list = ['one' , 'two' , 'three']
>>> newlist = list
>>> list.append('four')
>>> print newlist
['one', 'two', 'three', 'four']
That is the closest analogy to your dictionary code. You call a method on the original object.
The difference is that with your code you used a separate plus and assignment operator
list = list + ['four']
This is two separate operations. First the interpreter evaluates the expression list + ['four']. It must put the result of that computation in a new list object, because it does not anticipate that you will assign the result back to list. If you had said other_list = list + ['four'], you would have been very annoyed if list were modified.
Now there is a new object, containing the result of list + ['four']. That new object is assigned to list. list is now a reference to the new object, whereas newlist remains a reference to the old object.
Even this is different
list += ['four']
The += has the meaning for mutable object that it will modify the object in place.
Your two cases are doing different things to the objects you're copying, that's why you're seeing different results.
First off, you're not really copying them. Your simply making new "references" or (in more Pythonic terms) binding new names to the same objects.
With the dictionary, you're calling dict.clear, which discards all the contents. This modifies the existing object, so you see the results through both of the references you have to it.
With the list, you're rebinding one of the names to a new list. This new list is not the same as the old list, which remains unmodified.
You could recreate the behavior of your dictionary code with the lists if you want. A slice assignment is one way to modify a whole list at once:
old_list[:] = [] # empties the list in place
One addendum, unrelated to the main issue above: It's a very bad idea to use names like dict and list as variables in your own code. That's because those are the names of the builtin Python dictionary and list types. By using the same names, you shadow the built in ones, which can lead to confusing bugs.
In your dictionary example, you've created a dictionary and store it in dict. You then store the same reference in otherdict. Now both dict and otherdict point to the same dictionary*. Then you call dict.clear(). This clears the dictionary that both dict and otherdict point to.
In your list example, you've created a list and store it in list. You then store the same reference in otherlist. Then you create a new list consisting of the elements of list and another element and store the new list in list. You did not modify the original list you created. You created a new list and changed what list pointed to.
You can get your list example to show the same behavior as the dictionary example by using list.append('four') rather than list = list + ['four'].
Do you mean this?
>>> d = {'test1': 1, 'test2': 2}
>>> new_d = d
>>> new_d['test3'] = 3
>>> new_d
{'test1': 1, 'test3': 3, 'test2': 2}
>>> d # copied over
{'test1': 1, 'test3': 3, 'test2': 2}
>>> lst = [1, 2, 3]
>>> new_lst = lst
>>> new_lst.append(5)
>>> new_lst
[1, 2, 3, 5]
>>> lst # copied over
[1, 2, 3, 5]
>>> new_lst += [5]
>>> lst # copied over
[1, 2, 3, 5, 5]
>>> my_tuple = (1, 2, 3)
>>> new_my_tuple = my_tuple
>>> new_my_tuple += (5,)
>>> new_my_tuple
(1, 2, 3, 5)
>>> my_tuple # immutable, so it is not affected by new_my_tuple
(1, 2, 3)
Lists DO pass reference, not the object themselves. Most (hesitant on saying all) mutable (can be changed, such as lists and dictionaries) objects pass references, whereas immutable (cannot be changed, such as tuples) objects pass the object themselves.

What is this construct called in python: ( x, y )

What is this called in python:
[('/', MainPage)]
Is that an array .. of ... erhm one dictionary?
Is that
()
A tuple? ( or whatever they call it? )
Its a list with a single tuple.
Since no one has answered this bit yet:
A tuple? ( or whatever they call it? )
The word "tuple" comes from maths. In maths, we might talk about (ordered) pairs, if we're doing 2d geometry. Moving to three dimensions means we need triples. In higher dimensions, we need quadruples, quintuples, and, uh, whatever the prefix is for six, and so on. This starts to get to be a pain, and mathematicians also love generalising ("let's work in n dimensions today!"), so they started using the term "n-tuple" for an ordered list of n things (usually numbers).
After that, a bit of natural laziness is all you need to drop the "n-" and we end up with tuples.
Note that this:
("is not a tuple")
A tuple is defined by the commas, except in the case of the zero-length tuple. This:
"is a tuple",
because of the comma at the end. The parentheses just enforce grouping (again, except in the case of a zero-length tuple.
That's a list of tuples.
This is a list of integers: [1, 2, 3, 4, 5]
This is also a list of integers: [1]
This is a (string, integer) tuple: ("hello world", 42)
This is a list of (string, integer) tuples: [("a", 1), ("b", 2), ("c", 3)]
And so is this: [("a", 1)]
In Python, there's not much difference between lists and tuples. However, they are conceptually different. An easy way to think of it is that a list contains lots of items of the same type (homogeneous) , and a tuple contains a fixed number of items of different types (heterogeneous). An easy way to remember this is that lists can be appended to, and tuples cannot, because appending to a list makes sense and appending to a tuple doesn't.
Python doesn't enforce these distinctions -- in Python, you can append to a tuple with +, or store heterogeneous types in a list.
Yes, it's a tuple.
They look like this:
()
(foo,)
(foo, bar)
(foo, bar, baz)
etc.
[('/', MainPage)]
That's a list consisting of a two element tuple.
()
That's a zero element tuple.
It is a list of tuple(s). You can verify that by
x=[('/', MainPage)]
print type(x) # You will find a <list> type here
print type(x[0]) # You will find a <tuple> type here
You can build a dictionary from this type of structure (may be more tuple inside the list) with this code
my_dict = dict(x) # x=[('/',MainPage)]
It is a list of tuples containing one tuple.
A tuple is just like a list except that it is immutable, meaning that it can't be changed once it's created. You can't add, remove, or change elements in a tuple. If you want your tuple to be different, you have to create a new tuple with the new data. This may sound like a pain but in reality tuples have many benefits both in code safety and speed.
It's a list of just one tuple. That tuple has two elements, a string and the object MainPage whatever it is.
Both lists and tuples are ordered groups of object, it doesn't matter what kind of object, they can be heterogeneous in both cases.
The main difference between lists and tuples is that tuples are immutable, just like strings.
For example we can define a list and a tuple:
>>> L = ['a', 1, 5, 'b']
>>> T = ('a', 1, 5, 'b')
we can modify elements of L simply by assigning them a new value
>>> print L
['a', 1, 5, 'b']
>>> L[1] = 'c'
>>> print L
['a', 'c', 5, 'b']
This is not true for tuples
>>> print T
('a', 1, 5, 'b')
>>> T[1] = 'c'
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'tuple' object does not support item assignment
This is because they are immutable.
Tuples' elements may be mutable, and you can modify them, for example:
>>> T = (3, ['a', 1, 2], 'lol')
>>> T[1]
['a', 1, 2]
>>> T[1][0] = 'b'
>>> T
(3, ['b', 1, 2], 'lol')
but the list we edited is still the same object, we didn't replaced the tuple's element.

Categories