• Home   /  
  • Archive by category "1"

Tuple Object Does Not Support Item Assignment Of Contract

3.5. Python by Example¶

This section presents a crash course in the Python programming language through a series of examples. The bulk of the examples are derived directly from the first part of the official Python 2.7 Tutorial, modified somewhat to emphasize the features I tend to use in physical computing systems. This is not intended to replace any of the many excellent on-line Python courses available, it is only intended to serve as notes for an in-class tutorial or as a quick review aid for those who have used the language before.

Python has two distinct ‘current’ versions, since Python 3 introduced incompatible language changes, but many projects have stuck with the stable Python 2.7 version. Many OS distributions still only provide 2.7 by default in order to maintain compatibility with existing libraries and code. Right now, the latest stable releases available are Python 3.4.3 and Python 2.7.10.

I recommend using Python 2.7 for this course as that is the version under which the examples are tested. Our intention is that the library code should all eventually be cross-compatible. The following tutorial however is specific to 2.7 for ease of getting started.

3.5.1. Reference Links¶

If you are learning the language on your own or for the first time, you may wish to start with a full-featured tutorial or online course:

3.5.2. Preparation¶

Python is a text-based interpreted language and so tutorial presentation involves several ways of testing code:

  1. typing (or pasting) Python code directly to an interactive interpreter from the system command line (e.g. see Using the Python Interpreter)
  2. writing Python code in a syntax-aware text editor and then running it from the command line (e.g. see Exercise 0: The Setup)
  3. working within an Integrated Development Environment (IDE), a general term for a system for editing, running, and debugging code within one application (e.g. see One Day of IDLE Toying)

Just getting your computer set up to run your very first line of code can be a messy problem which stalls beginners. For simplicity, I will assume you are using IDLE, a simple IDE which comes standard with Python. It’s not very pretty, and most people outgrow it very soon, but it is an available tool which gets us past the first hurdle.

The standard computing platform for this course is described in IDeATe Virtual Cluster Software. On the cluster Macs, we will start by just running IDLE. Open a Terminal windows, then run it directly from the command line:

It will open a new window titled “Python 2.7.10 Shell” or similar, running as the Python application (which has a rocket-ship icon). The default font is very small, but may be changed via the Preferences dialog. Single lines of code can be typed or pasted directly into the command line of this shell, and any printed output will appear directly in the same window. So type the following into the shell window:

And the result will look similar to the following screenshot.

You’ll need to be able to save code in a file in order to do anything useful. The File/New File menu item will open a syntax-aware text editor to edit files. A syntax-aware editor can help you write correct code by highlighting language keywords, inserting correct indentation, and detecting some syntax errors. After saving the file (with a extension), it may be run using the Run/Run Module menu item (or F5). Console output will print in the shell window.

This is enough to get through the basic examples, although in the long run, you will need to figure out which tools are most productive for you. There are many other more fully-featured editors and IDEs.

print"Hello, world."print1+2

3.5.3. Using Python as a Calculator¶

Let’s try some simple Python commands. Start the interpreter and wait for the primary prompt, . (It shouldn’t take long.)

3.5.3.1. Numbers¶

The interpreter acts as a simple calculator: you can type an expression at it and it will write the value. Expression syntax is straightforward: the operators , , and work just like in most other languages (for example, Pascal or C); parentheses () can be used for grouping. For example:

The integer numbers (e.g. , , ) have type , the ones with a fractional part (e.g. , ) have type . We will see more about numeric types later in the tutorial.

With Python, it is possible to use the operator to calculate powers:

The equal sign () is used to assign a value to a variable. Afterwards, no result is displayed before the next interactive prompt:

If a variable is not “defined” (assigned a value), trying to use it will give you an error:

There is full support for floating point; operators with mixed type operands convert the integer operand to floating point:

In interactive mode, the last printed expression is assigned to the variable . This means that when you are using Python as a desk calculator, it is somewhat easier to continue calculations, for example:

This variable should be treated as read-only by the user.

>>> 2+24>>> 50-5*620>>> (50-5.0*6)/45.0>>> 8/5.01.6
>>> 5**2# 5 squared25>>> 2**7# 2 to the power of 7128
>>> width=20>>> height=5*9>>> width*height900
>>> n# try to access an undefined variableTraceback (most recent call last): File "<stdin>", line 1, in <module>NameError: name 'n' is not defined
>>> 3*3.75/1.57.5>>> 7.0/23.5
>>> tax=12.5/100>>> price=100.50>>> price*tax12.5625>>> price+_113.0625>>> round(_,2)113.06

3.5.3.2. Strings¶

Besides numbers, Python can also manipulate strings, which can be expressed in several ways. The following three ways of denoting strings produce exactly the same value:

There are also raw and Unicode strings which we won’t cover here.

The way to produce human-readable console output from within a program is the print command:

String literals can span multiple lines. One way is using triple-quotes:

produces the following output (note that the initial newline is not included):

Strings can be concatenated (glued together) with the operator, and repeated with :

Two or more string literals (i.e. the ones enclosed between quotes) next to each other are automatically concatenated.

If you want to concatenate variables or a variable and a literal, use :

This course will use arrays of numbers much more than strings, but many of the following methods for referring to sub-sequences are identical for both strings and other array types.

Strings can be indexed (subscripted), with the first character having index 0. There is no separate character type; a character is simply a string of size one:

Indices may also be negative numbers, to start counting from the right:

Note that since -0 is the same as 0, negative indices start from -1.

In addition to indexing, slicing is also supported. While indexing is used to obtain individual characters, slicing allows you to obtain a substring:

Note how the start is always included, and the end always excluded.

Slice indices have useful defaults; an omitted first index defaults to zero, an omitted second index defaults to the size of the string being sliced.

Python strings cannot be changed — they are immutable. Therefore, assigning to an indexed position in the string results in an error:

If you need a different string, you should create a new one:

The built-in function returns the length of a string:

>>> 'spam eggs'# single quotes'spam eggs'>>> "spam eggs"# double quotes'spam eggs'>>> """spam eggs"""# triple double-quotes'spam eggs'
>>> print'spam eggs'spam eggs
print"""\Usage: thingy [OPTIONS] -h Display this usage message -H hostname Hostname to connect to"""
Usage: thingy [OPTIONS] -h Display this usage message -H hostname Hostname to connect to
>>> # 3 times 'un', followed by 'ium'>>> 3*'un'+'ium''unununium'
>>> 'Py''thon''Python'
>>> prefix='Py'>>> prefix+'thon''Python'
>>> word='Python'>>> word[0]# character in position 0'P'>>> word[5]# character in position 5'n'
>>> word[-1]# last character'n'>>> word[-2]# second-last character'o'>>> word[-6]'P'
>>> word[0:2]# characters from position 0 (included) to 2 (excluded)'Py'>>> word[2:5]# characters from position 2 (included) to 5 (excluded)'tho'
>>> word[:2]+word[2:]'Python'>>> word[:4]+word[4:]'Python'
>>> word[:2]# character from the beginning to position 2 (excluded)'Py'>>> word[4:]# characters from position 4 (included) to the end'on'>>> word[-2:]# characters from the second-last (included) to the end'on'
>>> word[0]='J' ...TypeError: 'str' object does not support item assignment>>> word[2:]='py' ...TypeError: 'str' object does not support item assignment
>>> 'J'+word[1:]'Jython'>>> word[:2]+'py''Pypy'
>>> s='supercalifragilisticexpialidocious'>>> len(s)34

3.5.3.3. Lists¶

Python knows a number of compound data types, used to group together other values. The most versatile is the list, which can be written as a list of comma-separated values (items) between square brackets. Lists might contain items of different types, but usually the items all have the same type.

Like strings (and all other built-in sequence type), lists can be indexed and sliced:

All slice operations return a new list containing the requested elements. This means that the following slice returns a new (shallow) copy of the list:

Lists also supports operations like concatenation:

Unlike strings, which are immutable, lists are a mutable type, i.e. it is possible to change their content:

You can also add new items at the end of the list, by using the method (we will see more about methods later):

Assignment to slices is also possible, and this can even change the size of the list or clear it entirely:

The built-in function also applies to lists:

It is possible to nest lists (create lists containing other lists), for example:

>>> squares=[1,4,9,16,25]>>> squares[1, 4, 9, 16, 25]
>>> squares[0]# indexing returns the item1>>> squares[-1]25>>> squares[-3:]# slicing returns a new list[9, 16, 25]
>>> squares[:][1, 4, 9, 16, 25]
>>> squares+[36,49,64,81,100][1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
>>> cubes=[1,8,27,65,125]# something's wrong here>>> 4**3# the cube of 4 is 64, not 65!64>>> cubes[3]=64# replace the wrong value>>> cubes[1, 8, 27, 64, 125]
>>> cubes.append(216)# add the cube of 6>>> cubes.append(7**3)# and the cube of 7>>> cubes[1, 8, 27, 64, 125, 216, 343]
>>> letters=['a','b','c','d','e','f','g']>>> letters['a', 'b', 'c', 'd', 'e', 'f', 'g']>>> # replace some values>>> letters[2:5]=['C','D','E']>>> letters['a', 'b', 'C', 'D', 'E', 'f', 'g']>>> # now remove them>>> letters[2:5]=[]>>> letters['a', 'b', 'f', 'g']>>> # clear the list by replacing all the elements with an empty list>>> letters[:]=[]>>> letters[]
>>> letters=['a','b','c','d']>>> len(letters)4
>>> a=['a','b','c']>>> n=[1,2,3]>>> x=[a,n]>>> x[['a', 'b', 'c'], [1, 2, 3]]>>> x[0]['a', 'b', 'c']>>> x[0][1]'b'

3.5.4. First Steps Towards Programming¶

Of course, we can use Python for more complicated tasks than adding two and two together. For instance, we can write an initial sub-sequence of the Fibonacci series as follows:

This example introduces several new features.

  • The first line contains a multiple assignment: the variables and simultaneously get the new values 0 and 1. On the last line this is used again, demonstrating that the expressions on the right-hand side are all evaluated first before any of the assignments take place. The right-hand side expressions are evaluated from the left to the right.

  • The while loop executes as long as the condition (here: ) remains true. In Python, like in C, any non-zero integer value is true; zero is false. The condition may also be a string or list value, in fact any sequence; anything with a non-zero length is true, empty sequences are false. The test used in the example is a simple comparison. The standard comparison operators are written the same as in C: (less than), (greater than), (equal to), (less than or equal to), (greater than or equal to) and (not equal to).

  • The body of the loop is indented: indentation is Python’s way of grouping statements. At the interactive prompt, you have to type a tab or space(s) for each indented line. In practice you will prepare more complicated input for Python with a text editor; all decent text editors have an auto-indent facility. When a compound statement is entered interactively, it must be followed by a blank line to indicate completion (since the parser cannot guess when you have typed the last line). Note that each line within a basic block must be indented by the same amount.

  • The print statement writes the value of the expression(s) it is given. It differs from just writing the expression you want to write (as we did earlier in the calculator examples) in the way it handles multiple expressions and strings. Strings are printed without quotes, and a space is inserted between items, so you can format things nicely, like this:

    A trailing comma avoids the newline after the output:

    Note that the interpreter inserts a newline before it prints the next prompt if the last line was not completed.

    >>> i=256*256>>> print'The value of i is',iThe value of i is 65536
    >>> a,b=0,1>>> whileb<1000:... printb,... a,b=b,a+b...1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987

Besides the while statement just introduced, Python knows the usual control flow statements known from other languages, with some twists.

>>> # Fibonacci series:... # the sum of two elements defines the next... a,b=0,1>>> whileb<10:... printb... a,b=b,a+b...112358

3.5.5. Interlude: Running Code from Files¶

At this point in the standard Python tutorial, the examples start to include many multi-line program fragments, which are increasingly awkward to run by just pasting into the Python shell. I recommend running most of the following examples by editing them into individual small scripts saved as files; it will make them easier to modify.

To get you started, several of the examples are available in .

3.5.6. if Statements¶

Perhaps the most well-known statement type is the if statement. For example:

There can be zero or more elif parts, and the else part is optional. The keyword ‘elif‘ is short for ‘else if’, and is useful to avoid excessive indentation. An if ... elif ... elif ... sequence is a substitute for the or statements found in other languages.

x=int(raw_input("Please enter an integer: "))ifx<0:x=0print'Negative changed to zero'elifx==0:print'Zero'elifx==1:print'Single'else:print'More'

3.5.7. for Statements¶

Python’s for statement iterates over the items of any sequence (a list or a string), in the order that they appear in the sequence:

>>> words=['cat','window','defenestrate']>>> forwinwords:... printw,len(w)...cat 3window 6defenestrate 12

3.5.8. The Function¶

If you do need to iterate over a sequence of numbers, the built-in function comes in handy. It generates lists containing arithmetic progressions:

The given end point is never part of the generated list; generates a list of 10 values, the legal indices for items of a sequence of length 10.

To iterate over the indices of a sequence, you can combine and as follows:

In most such cases, however, it is convenient to use the function.

>>> range(10)[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> a=['Mary','had','a','little','lamb']>>> foriinrange(len(a)):... printi,a[i]...0 Mary1 had2 a3 little4 lamb

3.5.9. break and continue Statements, and else Clauses on Loops¶

The break statement breaks out of the smallest enclosing for or while loop.

Loop statements may have an clause; it is executed when the loop terminates through exhaustion of the list (with for) or when the condition becomes false (with while), but not when the loop is terminated by a break statement. This is exemplified by the following loop, which searches for prime numbers:

(Yes, this is the correct code. Look closely: the clause belongs to the for loop, not the if statement.)

When used with a loop, the clause has more in common with the clause of a try statement than it does that of if statements: a try statement’s clause runs when no exception occurs, and a loop’s clause runs when no occurs.

The continue statement, also borrowed from C, continues with the next iteration of the loop:

forninrange(2,10):forxinrange(2,n):ifn%x==0:printn,'equals',x,'*',n/xbreakelse:# loop fell through without finding a factorprintn,'is a prime number'
fornuminrange(2,10):ifnum%2==0:print"Found an even number",numcontinueprint"Found a number",num

3.5.10. pass Statements¶

The pass statement does nothing. It can be used when a statement is required syntactically but the program requires no action. For example:

This is commonly used for creating minimal classes:

Another place pass can be used is as a place-holder for a function or conditional body when you are working on new code, allowing you to keep thinking at a more abstract level. The pass is silently ignored:

>>> whileTrue:... pass# Busy-wait for keyboard interrupt (Ctrl+C)...
>>> classMyEmptyClass:... pass...
>>> definitlog(*args):... pass# Remember to implement this!...

3.5.11. Defining Functions¶

We can create a function that writes the Fibonacci series to an arbitrary boundary:

The keyword def introduces a function definition. It must be followed by the function name and the parenthesized list of formal parameters. The statements that form the body of the function start at the next line, and must be indented.

The first statement of the function body can optionally be a string literal; this string literal is the function’s documentation string, or docstring. (More about docstrings can be found in the section Documentation Strings.) There are tools which use docstrings to automatically produce online or printed documentation, or to let the user interactively browse through code; it’s good practice to include docstrings in code that you write, so make a habit of it.

The execution of a function introduces a new symbol table used for the local variables of the function. More precisely, all variable assignments in a function store the value in the local symbol table; whereas variable references first look in the local symbol table, then in the local symbol tables of enclosing functions, then in the global symbol table, and finally in the table of built-in names. Thus, global variables cannot be directly assigned a value within a function (unless named in a global statement), although they may be referenced.

The actual parameters (arguments) to a function call are introduced in the local symbol table of the called function when it is called; thus, arguments are passed using call by value (where the value is always an object reference, not the value of the object). When a function calls another function, a new local symbol table is created for that call.

A function definition introduces the function name in the current symbol table. The value of the function name has a type that is recognized by the interpreter as a user-defined function. This value can be assigned to another name which can then also be used as a function. This serves as a general renaming mechanism:

Coming from other languages, you might object that is not a function but a procedure since it doesn’t return a value. In fact, even functions without a return statement do return a value, albeit a rather boring one. This value is called (it’s a built-in name). Writing the value is normally suppressed by the interpreter if it would be the only value written. You can see it if you really want to using print:

It is simple to write a function that returns a list of the numbers of the Fibonacci series, instead of printing it:

This example, as usual, demonstrates some new Python features:

  • The return statement returns with a value from a function. return without an expression argument returns . Falling off the end of a function also returns .
  • The statement calls a method of the list object . A method is a function that ‘belongs’ to an object and is named , where is some object (this may be an expression), and is the name of a method that is defined by the object’s type. Different types define different methods. Methods of different types may have the same name without causing ambiguity. (It is possible to define your own object types and methods, using classes.) The method shown in the example is defined for list objects; it adds a new element at the end of the list. In this example it is equivalent to , but more efficient.
deffib(n):# write Fibonacci series up to n"""Print a Fibonacci series up to n."""a,b=0,1whilea<n:printa,a,b=b,a+b# Now call the function we just defined:fib(2000)
>>> fib<function fib at 10042ed0>>>> f=fib>>> f(100)0 1 1 2 3 5 8 13 21 34 55 89
>>> fib(0)>>> printfib(0)None
deffib2(n):# return Fibonacci series up to n"""Return a list containing the Fibonacci series up to n."""result=[]a,b=0,1whilea<n:result.append(a)# see belowa,b=b,a+breturnresultf100=fib2(100)# call itprintf100# write the result

3.5.12. More on Defining Functions¶

It is also possible to define functions with a variable number of arguments. There are three forms, which can be combined.

3.5.12.1. Default Argument Values¶

The most useful form is to specify a default value for one or more arguments. This creates a function that can be called with fewer arguments than it is defined to allow. For example:

This function can be called in several ways:

  • giving only the mandatory argument:
  • giving one of the optional arguments:
  • or even giving all arguments:

This example also introduces the in keyword. This tests whether or not a sequence contains a certain value.

defask_ok(prompt,retries=4,complaint='Yes or no, please!'):whileTrue:ok=raw_input(prompt)ifokin('y','ye','yes'):returnTrueifokin('n','no','nop','nope'):returnFalseretries=retries-1ifretries<0:raiseIOError('refusenik user')printcomplaint

3.5.12.2. Keyword Arguments¶

Functions can also be called using keyword arguments <keyword argument> of the form . For instance, the following function:

accepts one required argument () and three optional arguments (, , and ). This function can be called in any of the following ways:

but all the following calls would be invalid:

In a function call, keyword arguments must follow positional arguments. All the keyword arguments passed must match one of the arguments accepted by the function (e.g. is not a valid argument for the function), and their order is not important. This also includes non-optional arguments (e.g. is valid too). No argument may receive a value more than once. Here’s an example that fails due to this restriction:

defparrot(voltage,state='a stiff',action='voom',type='Norwegian Blue'):print"-- This parrot wouldn't",action,print"if you put",voltage,"volts through it."print"-- Lovely plumage, the",typeprint"-- It's",state,"!"
parrot(1000)# 1 positional argumentparrot(voltage=1000)# 1 keyword argumentparrot(voltage=1000000,action='VOOOOOM')# 2 keyword argumentsparrot(action='VOOOOOM',voltage=1000000)# 2 keyword argumentsparrot('a million','bereft of life','jump')# 3 positional argumentsparrot('a thousand',state='pushing up the daisies')# 1 positional, 1 keyword
parrot()# required argument missingparrot(voltage=5.0,'dead')# non-keyword argument after a keyword argumentparrot(110,voltage=220)# duplicate value for the same argumentparrot(actor='John Cleese')# unknown keyword argument
>>> deffunction(a):... pass...>>> function(0,a=0)Traceback (most recent call last): File "<stdin>", line 1, in ?TypeError: function() got multiple values for keyword argument 'a'

3.5.12.3. Documentation Strings¶

There are emerging conventions about the content and formatting of documentation strings.

The first line should always be a short, concise summary of the object’s purpose. For brevity, it should not explicitly state the object’s name or type, since these are available by other means (except if the name happens to be a verb describing a function’s operation). This line should begin with a capital letter and end with a period.

If there are more lines in the documentation string, the second line should be blank, visually separating the summary from the rest of the description. The following lines should be one or more paragraphs describing the object’s calling conventions, its side effects, etc.

The Python parser does not strip indentation from multi-line string literals in Python, so tools that process documentation have to strip indentation if desired. This is done using the following convention. The first non-blank line after the first line of the string determines the amount of indentation for the entire documentation string. (We can’t use the first line since it is generally adjacent to the string’s opening quotes so its indentation is not apparent in the string literal.) Whitespace “equivalent” to this indentation is then stripped from the start of all lines of the string. Lines that are indented less should not occur, but if they occur all their leading whitespace should be stripped. Equivalence of whitespace should be tested after expansion of tabs (to 8 spaces, normally).

Here is an example of a multi-line docstring:

defmy_function():"""Do nothing, but document it. No, really, it doesn't do anything. """pass

3.5.13. More on Lists¶

The list data type has some more methods. Here are all of the methods of list objects:

(x)

Add an item to the end of the list; equivalent to .

(L)

Extend the list by appending all the items in the given list; equivalent to .

(i, x)

Insert an item at a given position. The first argument is the index of the element before which to insert, so inserts at the front of the list, and is equivalent to .

(x)

Remove the first item from the list whose value is x. It is an error if there is no such item.

([i])

Remove the item at the given position in the list, and return it. If no index is specified, removes and returns the last item in the list. (The square brackets around the i in the method signature denote that the parameter is optional, not that you should type square brackets at that position. You will see this notation frequently in the Python Library Reference.)

(x)

Return the index in the list of the first item whose value is x. It is an error if there is no such item.

(x)

Return the number of times x appears in the list.

(cmp=None, key=None, reverse=False)

Sort the items of the list in place (the arguments can be used for sort customization, see for their explanation).

()

Reverse the elements of the list, in place.

An example that uses most of the list methods:

You might have noticed that methods like , or that only modify the list have no return value printed – they return the default . This is a design principle for all mutable data structures in Python.

3.5.13.1. List Comprehensions¶

List comprehensions provide a concise way to create lists. Common applications are to make new lists where each element is the result of some operations applied to each member of another sequence or iterable, or to create a subsequence of those elements that satisfy a certain condition.

For example, assume we want to create a list of squares, like:

We can obtain the same result with:

A list comprehension consists of brackets containing an expression followed by a for clause, then zero or more for or if clauses. The result will be a new list resulting from evaluating the expression in the context of the for and if clauses which follow it. For example, this listcomp combines the elements of two lists if they are not equal:

and it’s equivalent to:

Note how the order of the for and if statements is the same in both these snippets.

>>> squares=[]>>> forxinrange(10):... squares.append(x**2)...>>> squares[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
squares=[x**2forxinrange(10)]
>>> [(x,y)forxin[1,2,3]foryin[3,1,4]ifx!=y][(1, 3), (1, 4), (2, 3), (2, 1), (2, 4), (3, 1), (3, 4)]
>>> combs=[]>>> forxin[1,2,3]:... foryin[3,1,4]:... ifx!=y:... combs.append((x,y))...>>> combs[(1, 3), (1, 4), (2, 3), (2, 1), (2, 4), (3, 1), (3, 4)]
>>> a=[66.25,333,333,1,1234.5]>>> printa.count(333),a.count(66.25),a.count('x')2 1 0>>> a.insert(2,-1)>>> a.append(333)>>> a[66.25, 333, -1, 333, 1, 1234.5, 333]>>> a.index(333)1>>> a.remove(333)>>> a[66.25, -1, 333, 1, 1234.5, 333]>>> a.reverse()>>> a[333, 1234.5, 1, 333, -1, 66.25]>>> a.sort()>>> a[-1, 1, 66.25, 333, 333, 1234.5]>>> a.pop()1234.5>>> a[-1, 1, 66.25, 333, 333]

3.5.14. The del statement¶

There is a way to remove an item from a list given its index instead of its value: the del statement. This differs from the method which returns a value. The del statement can also be used to remove slices from a list or clear the entire list (which we did earlier by assignment of an empty list to the slice). For example:

del can also be used to delete entire variables:

Referencing the name hereafter is an error (at least until another value is assigned to it). We’ll find other uses for del later.

>>> a=[-1,1,66.25,333,333,1234.5]>>> dela[0]>>> a[1, 66.25, 333, 333, 1234.5]>>> dela[2:4]>>> a[1, 66.25, 1234.5]>>> dela[:]>>> a[]

3.5.15. Tuples and Sequences¶

We saw that lists and strings have many common properties, such as indexing and slicing operations. They are two examples of sequence data types. Since Python is an evolving language, other sequence data types may be added. There is also another standard sequence data type: the tuple.

A tuple consists of a number of values separated by commas, for instance:

As you see, on output tuples are always enclosed in parentheses, so that nested tuples are interpreted correctly; they may be input with or without surrounding parentheses, although often parentheses are necessary anyway (if the tuple is part of a larger expression). It is not possible to assign to the individual items of a tuple, however it is possible to create tuples which contain mutable objects, such as lists.

Though tuples may seem similar to lists, they are often used in different situations and for different purposes. Tuples are immutable, and usually contain an heterogeneous sequence of elements that are accessed via unpacking (see later in this section) or indexing. Lists are mutable, and their elements are usually homogeneous and are accessed by iterating over the list.

A special problem is the construction of tuples containing 0 or 1 items: the syntax has some extra quirks to accommodate these. Empty tuples are constructed by an empty pair of parentheses; a tuple with one item is constructed by following a value with a comma (it is not sufficient to enclose a single value in parentheses). Ugly, but effective. For example:

The statement is an example of tuple packing: the values , and are packed together in a tuple. The reverse operation is also possible:

This is called, appropriately enough, sequence unpacking and works for any sequence on the right-hand side. Sequence unpacking requires the list of variables on the left to have the same number of elements as the length of the sequence. Note that multiple assignment is really just a combination of tuple packing and sequence unpacking.

>>> t=12345,54321,'hello!'>>> t[0]12345>>> t(12345, 54321, 'hello!')>>> # Tuples may be nested:... u=t,(1,2,3,4,5)>>> u((12345, 54321, 'hello!'), (1, 2, 3, 4, 5))>>> # Tuples are immutable:... t[0]=88888Traceback (most recent call last): File "<stdin>", line 1, in <module>TypeError: 'tuple' object does not support item assignment>>> # but they can contain mutable objects:... v=([1,2,3],[3,2,1])>>> v([1, 2, 3], [3, 2, 1])
>>> empty=()>>> singleton='hello',# <-- note trailing comma>>> len(empty)0>>> len(singleton)1>>> singleton('hello',)

3.5.16. NumPy: Efficient Numerical Computation¶

The Python list and tuple types can represent the sequences and matrices of numbers we often use for signal-processing for physical computing. However, these are not very efficient when dealing with large arrays of numbers. For that we turn to NumPy, a library for scientific computing which provides powerful numeric array and linear algebra tools. NumPy is also the basis for SciPy, scikit-learn, and many other SciKits libraries which provide a vast set of tools for scientific computing.

NumPy is not included within the base Python installation but is included by default in many distributions and OS installations. To see if your interpreter includes numpy, try importing the library:

If that works properly, no error will be printed. Try creating some numeric arrays from the interpreter as follows:

numpy.arange(10)# array of numbers from 0 to 9numpy.array([0,1,2,3,4,5,6,7,8,9])# convert a Python sequence to a NumPy arraynumpy.array([[0,1,2],[3,4,5],[6,7,8]])# convert a Python sequence to a NumPy matrixnumpy.zeros(10)# create an array of zerosnumpy.eye(3)# create a 3x3 identity matrixnumpy.ndarray((2,3,4))# create an uninitialized 3D array shaped 2 x 3 x 4numpy.ndarray((2,3,4,5))# create an uninitialized 4D array shaped 2 x 3 x 4 x 5numpy.ndarray((2,3,4,5

My goal for today’s post is to give a deeper understanding of what tuples are and how those distinctions make them useful, saving their many novel applications involving packing/unpacking for a maybe-someday follow-up post so that I can address it a a psudo-distinct concept. Then, for those interested in what I’ve been writing, I hope to follow that up with tutorials on sequence types generally, lists specifically, usages of map, reduce and filter, and then the itertools module. Apparently my enthusiasm for sequences has a ways to go before it plays itself out. I make no promise of sticking to that schedule, but if I do I hope to release each of the above a slightly accelerated pace compared to my usual posting speed.

That all being said, for those of you who came here to learn (or better yet, teach me something!), I’ll get started on the tutorial now.

What They Are

Python tuples belong to the group of sequential data types, a subset of containers which is comprised of strings, Unicode strings, lists, tuples, bytearrays, buffers, and xrange objects. Tuples, like lists, can contain heterogenous ordered data; and all of the standard sequence operations can be applied to them. The primary difference between lists and tuples is that tuples are immutable, which means that once created, they cannot be altered. New tuples can be created from old tuples, and sub-tuples can be extracted, but once a tuple has been instantiated it is left utterly unchanged until the garbage collector cleans it up.

There are two very similar syntaxes for creating tuples, either just a comma separated list of values, or a comma separated list of values enclosed by technically optional parenthesis. I say ‘technically optional’ because to the very best of my understanding to exclude the parenthesis is uncommon and unpopular. I personally favour parenthesis as well, as I feel they keep visual consistency with lists while also clearly indicating that the created object is a tuple.

# Although you can do this: no_parens_tuple = 1, 'b', 2 # Personally I lean towards parens_tuple = ('a', 2, 'c')

What I feel helps my case in favour of the usage of parens is that the syntax to create a single item tuple is the element with a comma after it, with or without parens. Using just the comma without the parens is easy to miss and makes the code easier to misunderstand. To further emphasize my point, a zero-element tuple can only be created by just a pair of parenthesis with nothing between them. Given that these two uses both make a strong case for using the parens (even as uncommon as they may be), for the sake of consistency they should be used for longer tuples as well.

readable_tuple = ('spectacular',) ambiguous_tuple = 'spectacular', empty_tuple = ()

Please note that the trailing comma is required for a single item tuple, even if parenthesis are included. Otherwise the parens are disregarded and the result is just the object intended for the tuple.

remembered = (10,) remembered (10,) forgot = (10) forgot 10

The gathering of values into a tuple is called ‘tuple packing.’ The converse also exists, called ‘tuple unpacking,’ and I’ll delve into it in part two of this series. It’s a concept that deserves individual addressing.

Tuples have a couple more interesting (or challenging) characteristics that are born of their immutable nature. Firstly, they are hashable, which means (amongst other things) that they can be used as keys in dictionaries. Were you to try using a list as a key in a dict it would fail, and with good reason; as soon as the list was altered its hash value would change and it would point to a different or nonexistent bucket of items in the dictionary. To be honest, there aren’t many cases where hashing a tuple is going to be essential, but there’s a pretty healthy number where it’s neat, intuitive, or more pythonic than the alternative.

Functionally, it sometimes helps to think of tuples as a subset of lists; they forgo all the non-standard-sequence functions (such as append() and index assignment) but in exchange are a faster and more lightweight data type. If performance is a concern and it’s an option, tuples are the way to go. Before further addressing when and why to use tuples, I’d like to illustrate a little further the distinctions between mutable lists and immutable tuples below:

person_tuple = ('James Woods', 45, 'Probably Hollywood', 'Actor') person_list = ['Luke Skywalker', 25, 'Far Far Away', 'Jedi Knight'] # Of course appending to a list works like you'd expect person_list.append('Yoda's School of the Jedi Arts') # But we can't do this with tuples person_tuple.append('School of Hard Knocks') Traceback (most recent call last): File '<stdin>', line 1, in <module> AttributeError: 'tuple' object has no attribute 'append' # Assigning new values doesn't work either person_tuple[3] = 'Celebrity' Traceback (most recent call last): File '<stdin>', line 1, in <module> TypeError: 'tuple' object does not support item assignment # Tuples can be used to derive new tuples: slice_tuple = person_tuple[1:3] # Using slices combined_tuple = person_tuple + person_tuple # Appending one to another copied_tuple = person_tuple[:] # Duplicating tuple tuple_val = person_tuple[0] # Index retrieval works as you'd expect

 

When To Use Them

Because lists and tuples are so similar, and lists grant additional flexibility, it’s easily to fall back on the argument that lists will do just fine, particularly because they’re so comfy and familiar to most people by the time they discover tuples. And to be honest, a great majority of the time using lists will be perfectly adequate, and if by the end of this tutorial you’re still confused by tuples and unsure of where they belong, go ahead and use that list, it’s okay. Chances are it’ll work just fine and no one will ever even think to question you. That being said, there are times that the advantages of tuples (such as their immutability, hashability, and performance gains) are significant enough to justify their usage. My goal here is to give you just a bit of a sense of when these cases might be.

You may note that many of my examples so far have contained heterogenous data constructed to represent a complex object (like a person). This is because unlike with lists we have a guaranteed contract of consistency with tuples, meaning that if I create a tuple with a string, a number, and then a bytearray, I can know with absolute certainty when I operate on that tuple later that the properties and their types will match the schema and values with which I created them earlier. Because there is no guarantee of order or data type with a list after it’s been created you lose that contract of consistency; and therefor I find that as a rule of thumb although lists techinically support heterogenous data, I try to keep them to sets of a single data type and use them in cases where I’m iterating over the elements and performing a uniform operation on all of them.

Tuples on the other hand are write-protected, almost like they have an implicit assert statement verifying that their data will be correctly formed. For this reason I use them typically for cases where I need a quick stand-in for an object, one that’s light-weight, easy reconfigurable at a later date, and not needed for use elsewhere. For example in a quick script to pull rows of data from a static source (database, csv, or other similar store) where you’re doing all the heavy lifting for one reason or another, it makes sense to handle rows as tuples because it wouldn’t make sense to modify, reorder, or resize those rows; it would create inconsistency of data that would be difficult to process. If our rows are tuples then when we use code like the following:

for row_num, name, email, first_pet, mothers_maiden_name in rows: print 'Processing %s (%d/%d)' % (name, row_num, len(rows)) recover_password(email, first_pet, mothers_maiden_name)

We know our code won’t fail because of an accidentally extended row or a swapped name and email (unless of course the original data was corrupt, which is a whole different story). Python itself is written around this concept, that tuples have an implied semantic and an index has meaning. A great example of this is the return value of python’s database API specification, where fetchmany() and fetchall() return a list (ordered sequence of homogenous data) of tuples representing rows (hetergenous set where order has specific meaning). Cool, eh?

Another of tuples’ advantages needs less longwinded addressing, their improved performance, as it is relatively self explanatory. More lightweight than lists, dictionaries, or instantiating custom objects, creating and iterating over tuples is simply faster. Of course, this isn’t carte blanche, wrapping a list in a one-element tuple won’t speed up processing of that list, but for tuples that contain only hashable data types (simple and immutable types) there’s a pretty decent speed boost. Rarely will you find yourself writing python where the difference between list and tuples performance is a make or break choice, but if you’re dealing with large amounts of performance sensitive code and data, sticking to tuples can be one of many choices you make to make sure it all runs at top speed.

Finally, the hashability of tuples is another simple but neat little feature. This, like the performance gains, is particular to tuples that contain hashable data, but can be applied in some pretty fun places. Generally, it makes any situation where you have a couple of shared properties between objects that you would like to group and access them by a lot easier. For example, lat/long coordinate pairs for people or residencies, or month_of_year, day_of_month pairs make it easy to group date-tracked data while neatly handling that pesky leap-year problem.

lat_long_tup = (49, -117) lat_long_list = [37, -122] # If I were storing a bunch of house locations, it'd be easy to group them by lat/long coordinates. # I do this by coordinates instead of by city because I used to live in a forest and no, # Vancouver isn't a close enough approximation. It's eight hours away! peoples_houses = {} peoples_houses[lat_long_tup] = [my_old_house, my_old_neighbour] #This will fail due to lists being unhashable peoples_houses[lat_long_list] = [my_new_house, my_old_neighbour] Traceback (most recent call last): File '', line 1, in TypeError: unhashable type: 'list'

Extra Reading

A shorter, better, and cooler description of tuples and how they compare to lists
An excellent tuple tutorial, and it’s illustrated!
Sequence types. The table in this section is an invaluable reference
Python tuples aren’t just constant lists

One thought on “Tuple Object Does Not Support Item Assignment Of Contract

Leave a comment

L'indirizzo email non verrà pubblicato. I campi obbligatori sono contrassegnati *