6.18. Special Features of Tuples6.18.1. How Are Tuples Affected by Immutability?Okay, we have been throwing around this word "immutable" in many parts of the text. Aside from its computer science definition and implications, what is the bottom line as far as applications are concerned? What are all the consequences of an immutable data type? Of the three standard types that are immutablenumbers, strings, and tuplestuples are the most affected. A data type that is immutable simply means that once an object is defined, its value cannot be updated, unless, of course, a completely new object is allocated. The impact on numbers and strings is not as great since they are scalar types, and when the sole value they represent is changed, that is the intended effect, and access occurs as desired. The story is different with tuples, however. Because tuples are a container type, it is often desired to change single or multiple elements of that container. Unfortunately, this is not possible. Slice operators cannot show up on the left-hand side of an assignment. Recall this is no different for strings, and that slice access is used for read access only. Immutability does not necessarily mean bad news. One bright spot is that if we pass in data to an API with which we are not familiar, we can be certain that our data will not be changed by the function called. Also, if we receive a tuple as a return argument from a function that we would like to manipulate, we can use the list() built-in function to turn it into a mutable list. 6.18.2. Tuples Are Not Quite So "Immutable"Although tuples are defined as immutable, this does not take away from their flexibility. Tuples are not quite as immutable as we made them out to be. What do we mean by that? Tuples have certain behavioral characteristics that make them seem not as immutable as we had first advertised. For example, we can join strings together to form a larger string. Similarly, there is nothing wrong with putting tuples together to form a larger tuple, so concatenation works. This process does not involve changing the smaller individual tuples in any way. All we are doing is joining their elements together. Some examples are presented here: >>> s = 'first' >>> s = s + ' second' >>> s 'first second' >>> >>> t = ('third', 'fourth') >>> t ('third', 'fourth') >>> >>> t = t + ('fifth', 'sixth') >>> t ('third', 'fourth', 'fifth', 'sixth') The same concept applies for repetition. Repetition is just concatenation of multiple copies of the same elements. In addition, we mentioned in the previous section that one can turn a tuple into a mutable list with a simple function call. Our final feature may surprise you the most. You can "modify" certain tuple elements. Whoa. What does that mean? Although tuple objects themselves are immutable, this fact does not preclude tuples from containing mutable objects that can be changed. >>> t = (['xyz', 123], 23, -103.4) >>> t (['xyz', 123], 23, -103.4) >>> t[0][1] 123 >>> t[0][1] = ['abc', 'def'] >>> t (['xyz', ['abc', 'def']], 23, -103.4) In the above example, although t is a tuple, we managed to "change" it by replacing an item in the first tuple element (a list). We replaced t[0][1], formerly an integer, with a list ['abc', 'def']. Although we modified only a mutable object, in some ways, we also "modified" our tuple. 6.18.3. Default Collection TypeAny set of multiple objects, comma-separated, written without identifying symbols, i.e., brackets for lists, parentheses for tuples, etc., defaults to tuples, as indicated in these short examples: >>> 'abc', -4.24e93, 18+6.6j, 'xyz' ('abc', -4.24e+093, (18+6.6j), 'xyz') >>> >>> x, y = 1, 2 >>> x, y (1, 2) Any function returning multiple objects (also no enclosing symbols) is a tuple. Note that enclosing symbols change a set of multiple objects returned to a single container object. For example: def foo1(): : return obj1, obj2, obj3 def foo2(): : return [obj1, obj2, obj3] def foo3(): : return (obj1, obj2, obj3) In the above examples, foo1() calls for the return of three objects, which come back as a tuple of three objects, foo2() returns a single object, a list containing three objects, and foo3() returns the same thing as foo1(). The only difference is that the tuple grouping is explicit. Explicit grouping of parentheses for expressions or tuple creation is always recommended to avoid unpleasant side effects: >>> 4, 2 < 3, 5 # int, comparison, int (4, True, 5) >>> (4, 2) < (3, 5) # tuple comparison False In the first example, the less than ( < ) operator took precedence over the comma delimiter intended for the tuples on each side of the less than sign. The result of the evaluation of 2 < 3 became the second element of a tuple. Properly enclosing the tuples enables the desired result. 6.18.4. Single-Element TuplesEver try to create a tuple with a single element? You tried it with lists, and it worked, but then you tried and tried with tuples, but you cannot seem to do it. >>> ['abc'] ['abc'] >>> type(['abc']) # a list <type 'list'> >>> >>> ('xyz') 'xyz' >>> type(('xyz')) # a string, not a tuple <type 'str'> It probably does not help your case that the parentheses are also overloaded as the expression grouping operator. Parentheses around a single element take on that binding role rather than serving as a delimiter for tuples. The workaround is to place a trailing comma (,) after the first element to indicate that this is a tuple and not a grouping. >>> ('xyz',) ('xyz',) 6.18.5. Dictionary KeysImmutable objects have values that cannot be changed. That means that they will always hash to the same value. That is the requirement for an object being a valid dictionary key. As we will find out in the next chapter, keys must be hashable objects, and tuples meet that criteria. Lists are not eligible. Core Note: Lists versus Tuples
|