I l@ve RuBoard

### C.2 Chapter 2

1. The basics. Here are the sort of results you should get, along with a few comments about their meaning:

`Numbers`
```>>> 2 ** 16              ?/b># 2 raised to the power 16
65536
>>> 2 / 5, 2 / 5.0       ?/B># integer / truncates, float / doesn't
(0, 0.4)

燬trings```
```>>> "spam" + "eggs"     ?/b> # concatenation
'spameggs'
>>> S = "ham"?/b>
>>> "eggs " + S?/B>
'eggs ham'
>>> S * 5               ?/B> # repetition
'hamhamhamhamham'
>>> S[:0]               ?/b> # an empty slice at the front--[0:0]
''
>>> "green %s and %s" % ("eggs", S)?/b>  # formatting
'green eggs and ham'

燭uples```
```>>> ('x',)[0]                       ?/b> # indexing a single-item tuple
'x'
>>> ('x', 'y')[1]                   ?/b> # indexing a 2-item tuple
'y'

燣ists```
```>>> L = [1,2,3] + [4,5,6]           ?/b> # list operations
>>> L, L[:], L[:0], L[-2], L[-2:]?/b>
([1, 2, 3, 4, 5, 6], [1, 2, 3, 4, 5, 6], [], 5, [5, 6])
>>> ([1,2,3]+[4,5,6])[2:4]?/b>
[3, 4]
>>> [L[2], L[3]]                     ?/b># fetch from offsets, store in a list
[3, 4]
>>> L.reverse(); L                  ?/b> # method: reverse list in-place
[6, 5, 4, 3, 2, 1]
>>> L.sort(); L                     ?/B> # method: sort list in-place
[1, 2, 3, 4, 5, 6]
>>> L.index(4)                       ?/B># method: offset of first 4 (search)
3

燚ictionaries```
```>>> {'a':1, 'b':2}['b']             ?/B> # index a dictionary by key
2
>>> D = {'x':1, 'y':2, 'z':3}?/b>
>>> D['w'] = 0                       ?/b># create a new entry
>>> D['x'] + D['w']?/B>
1
>>> D[(1,2,3)] = 4                   ?/B># a tuple used as a key (immutable)
>>> D?/b>
{'w': 0, 'z': 3, 'y': 2, (1, 2, 3): 4, 'x': 1}
>>> D.keys(), D.values(), D.has_key((1,2,3))         ?/b> # methods
(['w', 'z', 'y', (1, 2, 3), 'x'], [0, 3, 2, 4, 1], 1)

燛mpties```
```>>> [[]], ["",[],(),{},None]                          # lots of nothings
([[]], ['', [], (), {}, None])```
2. Indexing and slicing.

1. Indexing out-of-bounds (e.g., L[4]) raises an error; Python always checks to make sure that all offsets are within the bounds of a sequence (unlike C, where out-of-bound indexes will happily crash your system).

2. On the other hand, slicing out of bounds (e.g., L[-1000:100]) works, because Python scales out-of-bounds slices so that they always fit (they're set to zero and the sequence length, if required).

3. Extracting a sequence in reverse梬ith the lower bound > the higher bound (e.g., L[3:1])梔oesn't really work. You get back an empty slice ([]), because Python scales the slice limits to makes sure that the lower bound is always less than or equal to the upper bound (e.g., L[3:1] is scaled to L[3:3], the empty insertion point after offset 3). Python slices are always extracted from left to right, even if you use negative indexes (they are first converted to positive indexes by adding the length).

```>>> L = [1, 2, 3, 4]
>>> L[4]
Traceback (innermost last):
File "<stdin>", line 1, in ?
IndexError: list index out of range
>>> L[-1000:100]
[1, 2, 3, 4]
>>> L[3:1]
[]
>>> L
[1, 2, 3, 4]
>>> L[3:1] = ['?']
>>> L
[1, 2, 3, '?', 4]```
3. Indexing, slicing, and del. Your interaction with the interpreter should look something like that listed below. Note that assigning an empty list to an offset stores an empty list object there, but assigning it to a slice deletes the slice. Slice assignment expects another sequence, or you'll get a type error.

```>>> L = [1,2,3,4]
>>> L[2] = []
>>> L
[1, 2, [], 4]
>>> L[2:3] = []
>>> L
[1, 2, 4]
>>> del L[0]
>>> L
[2, 4]
>>> del L[1:]
>>> L
[2]
>>> L[1:2] = 1
Traceback (innermost last):
File "<stdin>", line 1, in ?
TypeError: illegal argument type for built-in operation```
4. Tuple assignment. The values of X and Y are swapped. When tuples appear on the left and right of an assignment operator (=), Python assigns objects on the right to targets on the left, according to their positions. This is probably easiest to understand by noting that targets on the left aren't a real tuple, even though they look like one; they are simply a set of independent assignment targets. The items on the right are a tuple, which get unpacked during the assignment (the tuple provides the temporary assignment needed to achieve the swap effect).

```>>> X = 'spam'
>>> Y = 'eggs'
>>> X, Y = Y, X
>>> X
'eggs'
>>> Y
'spam'```
5. Dictionary keys. Any immutable object can be used as a dictionary key梚ntegers, tuples, strings, and so on. This really is a dictionary, even though some of its keys look like integer offsets. Mixed type keys work fine too.

```>>> D = {}
>>> D[1] = 'a'
>>> D[2] = 'b'
>>> D[(1, 2, 3)] = 'c'
>>> D
{1: 'a', 2: 'b', (1, 2, 3): 'c'}```
6. Dictionary indexing. Indexing a nonexistent key (D['d']) raises an error; assigning to a nonexistent key (D['d'] = 'spam') creates a new dictionary entry. On the other hand, out-of-bounds indexing for lists raises an error too, but so do out-of-bounds assignments. Variable names work like dictionary keys: they must have already been assigned when referenced, but are created when first assigned. In fact, variable names can be processed as dictionary keys if you wish (they're visible in module namespace or stack-frame dictionaries).

```>>> D = {'a':1, 'b':2, 'c':3}
>>> D['a']
1
>>> D['d']
Traceback (innermost last):
File "<stdin>", line 1, in ?
KeyError: d
>>> D['d'] = 4
>>> D
{'b': 2, 'd': 4, 'a': 1, 'c': 3}
>>>
>>> L = [0,1]
>>> L[2]
Traceback (innermost last):
File "<stdin>", line 1, in ?
IndexError: list index out of range
>>> L[2] = 3
Traceback (innermost last):
File "<stdin>", line 1, in ?
IndexError: list assignment index out of range```
7. Generic operations.

1. The + operator doesn't work on different/mixed types (e.g., string + list, list + tuple).

2. + doesn't work for dictionaries, because they aren't sequences.

3. The append method works only for lists, not strings, and keys works only on dictionaries. append assumes its target is mutable, since it's an in-place extension; strings are immutable.

4. Slicing and concatenation always return a new object of the same type as the objects processed.

```>>> "x" + 1
Traceback (innermost last):
File "<stdin>", line 1, in ?
TypeError: illegal argument type for built-in operation
>>>
>>> {} + {}
Traceback (innermost last):
File "<stdin>", line 1, in ?
TypeError: bad operand type(s) for +
>>>
>>> [].append(9)
>>> "".append('s')
Traceback (innermost last):
File "<stdin>", line 1, in ?
AttributeError: attribute-less object
>>>
>>> {}.keys()
[]
>>> [].keys()
Traceback (innermost last):
File "<stdin>", line 1, in ?
AttributeError: keys
>>>
>>> [][:]
[]
>>> ""[:]
''```
8. String indexing. Since strings are collections of one-character strings, every time you index a string, you get back a string, which can be indexed again. S[0][0][0][0][0] just keeps indexing the first character over and over. This generally doesn't work for lists (lists can hold arbitrary objects), unless the list contains strings.

```>>> S = "spam"
>>> S[0][0][0][0][0]
's'
>>> L = ['s', 'p']
>>> L[0][0][0]
's'```
9. Immutable types. Either of the solutions below work. Index assignment doesn't, because strings are immutable.

```>>> S = "spam"
>>> S = S[0] + 'l' + S[2:]
>>> S
'slam'
>>> S = S[0] + 'l' + S[2] + S[3]
>>> S
'slam'```
10. Nesting. Your mileage will vary.

```>>> me = {'name':('mark', 'e', 'lutz'), 'age':'?', 'job':'engineer'}
>>> me['job']
'engineer'
>>> me['name'][2]
'lutz'```
11. Files.

```% cat maker.py
file = open('myfile.txt', 'w')
file.write('Hello file world!\n')
file.close()                         # close not always needed

file = open('myfile.txt', 'r')

% python maker.py
```>>> [].__methods__