Session Three: Sequences, Iteration and String Formatting¶
Review/Questions¶
Review of Previous Session¶
- Functions
- recursion
- optional arguments
- Booleans
- if and conditional expressions
- Modules
git¶
OK – we’ll answer git questions...
Sequences¶
Ordered collections of objects
What is a Sequence?¶
Remember Duck Typing?
A sequence can be considered as anything that supports at least these operations:
- Indexing
- Slicing
- Membership
- Concatenation
- Length
- Iteration
Sequence Types¶
There are eight builtin types in Python that are sequences:
- string
- list
- tuple
- bytes
- bytearray
- buffer
- array.array
- range object (almost)
For this class, you won’t see much beyond string, lists, and tuples – the rest are pretty special purpose.
But what we learn today applies to all sequences (with minor caveats)
Indexing¶
Items in a sequence may be looked up by index using the indexing
operator: []
Indexing in Python always starts at zero.
In [98]: s = "this is a string"
In [99]: s[0]
Out[99]: 't'
In [100]: s[5]
Out[100]: 'i'
You can use negative indexes to count from the end:
In [2]: a_list = [34, 56, 19, 23, 55]
In [3]: a_list[-1]
Out[3]: 55
In [4]: a_list[-2]
Out[4]: 23
In [5]: a_list[-4]
Out[5]: 56
Indexing beyond the end of a sequence causes an IndexError:
In [6]: a_list
Out[6]: [34, 56, 19, 23, 55]
In [7]: a_list[5]
---------------------------------------------------------------------------
IndexError Traceback (most recent call last)
<ipython-input-7-c1f9ac3b6fee> in <module>()
----> 1 a_list[5]
IndexError: list index out of range
Slicing¶
Slicing a sequence creates a new sequence with a range of objects from the original sequence.
It also uses the indexing operator ([]
), but with a twist.
sequence[start:finish]
returns all sequence[i] for which start <= i < finish:
In [121]: s = "a bunch of words"
In [122]: s[2]
Out[122]: 'b'
In [123]: s[6]
Out[123]: 'h'
In [124]: s[2:6]
Out[124]: 'bunc'
In [125]: s[2:7]
Out[125]: 'bunch'
Think of the indexes as pointing to the spaces between the items:
a b u n c h o f w o r d s
| | | | | | | | | | | | | | | |
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
You do not have to provide both start
and finish
:
In [6]: s = "a bunch of words"
In [7]: s[:5]
Out[7]: 'a bun'
In [8]: s[5:]
Out[8]: 'ch of words'
Either 0
or len(s)
will be assumed, respectively.
You can combine this with the negative index to get the end of a sequence:
In [4]: s = 'this_could_be_a_filename.txt'
In [5]: s[:-4]
Out[5]: 'this_could_be_a_filename'
In [6]: s[-4:]
Out[6]: '.txt'
Why start from zero?¶
Python indexing feels ‘weird’ to some folks – particularly those that don’t come with a background in the C family of languages.
Why is the “first” item indexed with zero?
Why is the last item in the slice not included?
Because these lead to some nifty properties:
len(seq[a:b]) == b-a
seq[:b] + seq[b:] == seq
len(seq[:b]) == b
len(seq[-b:]) == b
There are very many fewer “off by one” errors as a result.
Slicing takes a third argument, step
which controls which items are
returned:
In [18]: a_tuple
Out[18]: (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19)
In [19]: a_tuple[0:15]
Out[19]: (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14)
In [20]: a_tuple[0:15:2]
Out[20]: (0, 2, 4, 6, 8, 10, 12, 14)
In [21]: a_tuple[0:15:3]
Out[21]: (0, 3, 6, 9, 12)
In [22]: a_tuple[::-1]
Out[22]: (19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0)
Though they share an operator, slicing and indexing have a few important differences:
Indexing will always return one object, slicing will return a sequence of objects.
Indexing past the end of a sequence will raise an error, slicing will not:
In [129]: s = "a bunch of words"
In [130]: s[17]
----> 1 s[17]
IndexError: string index out of range
In [131]: s[10:20]
Out[131]: ' words'
In [132]: s[20:30]
Out[132]: "
Membership¶
All sequences support the in
and not in
membership operators:
In [15]: s = [1, 2, 3, 4, 5, 6]
In [16]: 5 in s
Out[16]: True
In [17]: 42 in s
Out[17]: False
In [18]: 42 not in s
Out[18]: True
For strings, the membership operations are like substring
operations in
other languages:
In [20]: s = "This is a long string"
In [21]: "long" in s
Out[21]: True
This does not work for sub-sequences of other types (can you think of why?):
In [22]: s = [1, 2, 3, 4]
In [23]: [2, 3] in s
Out[23]: False
Concatenation¶
Using +
or *
on sequences will concatenate them:
In [18]: l1 = [1,2,3,4]
In [19]: l2 = [5,6,7,8]
In [20]: l1 + l2
Out[20]: [1, 2, 3, 4, 5, 6, 7, 8]
In [21]: (l1+l2) * 2
Out[21]: [1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8]
You can apply this concatenation to slices as well, leading to some nicely concise code:
from CodingBat: Warmup-1 – front3
def front3(str):
if len(str) < 3:
return str+str+str
else:
return str[:3]+str[:3]+str[:3]
This non-pythonic solution can also be expressed like so:
def front3(str):
return str[:3] * 3
Length¶
All sequences have a length. You can get it with the len
builtin:
In [36]: s = "how long is this, anyway?"
In [37]: len(s)
Out[37]: 25
Remember: Sequences are 0-indexed, so the last index is len(s)-1
:
In [38]: count = len(s)
In [39]: s[count]
------------------------------------------------------------
IndexError Traceback (most recent call last)
<ipython-input-39-5a33b9d3e525> in <module>()
----> 1 s[count]
IndexError: string index out of range
Even better: use s[-1]
Miscellaneous¶
There are a more operations supported by all sequences
All sequences also support the min
and max
builtins:
In [42]: all_letters = "thequickbrownfoxjumpedoverthelazydog"
In [43]: min(all_letters)
Out[43]: 'a'
In [44]: max(all_letters)
Out[44]: 'z'
Why are those the answers you get? (hint: ord('a')
)
Of course this works with numbers, too!
All sequences also support the index
method, which returns the index of the first occurence of an item in the sequence:
In [46]: all_letters.index('d')
Out[46]: 21
This causes a ValueError
if the item is not in the sequence:
In [47]: all_letters.index('A')
---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
<ipython-input-47-2db728a46f78> in <module>()
----> 1 all_letters.index('A')
ValueError: substring not found
A sequence can also be queried for the number of times a particular item appears:
In [52]: all_letters.count('o')
Out[52]: 4
In [53]: all_letters.count('the')
Out[53]: 2
This does not raise an error if the item you seek is not present:
In [54]: all_letters.count('A')
Out[54]: 0
Lists, Tuples...¶
The primary sequence types.
Lists¶
Lists can be constructed using list Literals ([]
):
In [1]: []
Out[1]: []
In [2]: [1,2,3]
Out[2]: [1, 2, 3]
In [3]: [1, 'a', 7.34]
Out[3]: [1, 'a', 7.34]
Or by using the list
type object as a constructor:
In [6]: list()
Out[6]: []
In [7]: list(range(4))
Out[7]: [0, 1, 2, 3]
In [8]: list('abc')
Out[8]: ['a', 'b', 'c']
It will take any “iterable”
The elements contained in a list need not be of a single type.
Lists are heterogenous, ordered collections.
Each element in a list is a value, and can be in multiple lists and have multiple names (or no name)
In [9]: name = 'Brian'
In [10]: a = [1, 2, name]
In [11]: b = [3, 4, name]
In [12]: a[2]
Out[12]: 'Brian'
In [13]: b[2]
Out[13]: 'Brian'
In [14]: a[2] is b[2]
Out[14]: True
Tuples¶
Tuples can be constructed using tuple literals (()
):
In [15]: ()
Out[15]: ()
In [16]: (1, 2)
Out[16]: (1, 2)
In [17]: (1, 'a', 7.65)
Out[17]: (1, 'a', 7.65)
In [18]: (1,)
Out[18]: (1,)
Tuples don’t NEED parentheses...
In [161]: t = (1,2,3)
In [162]: t
Out[162]: (1, 2, 3)
In [163]: t = 1,2,3
In [164]: t
Out[164]: (1, 2, 3)
In [165]: type(t)
Out[165]: tuple
But they do need commas...!
In [156]: t = ( 3 )
In [157]: type(t)
Out[157]: int
In [158]: t = ( 3, )
In [160]: type(t)
Out[160]: tuple
You can also use the tuple
type object to convert any iterable(sequence) into a tuple:
In [20]: tuple()
Out[20]: ()
In [21]: tuple(range(4))
Out[21]: (0, 1, 2, 3)
In [22]: tuple('garbanzo')
Out[22]: ('g', 'a', 'r', 'b', 'a', 'n', 'z', 'o')
The elements contained in a tuple need not be of a single type.
Tuples are heterogenous, ordered collections.
Each element in a tuple is a value, and can be in multiple tuples and have multiple names (or no name)
In [23]: name = 'Brian'
In [24]: other = name
In [25]: a = (1, 2, name)
In [26]: b = (3, 4, other)
In [27]: for i in range(3):
....: print(a[i] is b[i], end=' ')
....:
False False True
So Why Have Both?
Mutability¶
image from flickr by illuminaut, (CC by-nc-sa)
Mutability in Python¶
All objects in Python fall into one of two camps:
- Mutable
- Immutable
Objects which are mutable may be changed in place.
Objects which are immutable may not be changed.
Ever.
Immutable | Mutable |
---|---|
Unicode | List |
String | |
Integer | |
Float | |
Tuple |
Try this out:
In [28]: food = ['spam', 'eggs', 'ham']
In [29]: food
Out[29]: ['spam', 'eggs', 'ham']
In [30]: food[1] = 'raspberries'
In [31]: food
Out[31]: ['spam', 'raspberries', 'ham']
And repeat the exercise with a Tuple:
In [32]: food = ('spam', 'eggs', 'ham')
In [33]: food
Out[33]: ('spam', 'eggs', 'ham')
In [34]: food[1] = 'raspberries'
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-34-0c3401794933> in <module>()
----> 1 food[1] = 'raspberries'
TypeError: 'tuple' object does not support item assignment
This property means you need to be aware of what you are doing with your lists:
In [36]: original = [1, 2, 3]
In [37]: altered = original
In [38]: for i in range(len(original)):
....: if True:
....: altered[i] += 1
....:
Perhaps we want to check to see if altered has been updated, as a flag for whatever condition caused it to be updated.
What is the result of this code?
Our altered
list has been updated:
In [39]: altered
Out[39]: [2, 3, 4]
But so has the original
list:
In [40]: original
Out[40]: [2, 3, 4]
Why?
Easy container setup, or deadly trap?
(note: you can nest lists to make a 2D-ish array)
In [13]: bins = [ [] ] * 5
In [14]: bins
Out[14]: [[], [], [], [], []]
In [15]: words = ['one', 'three', 'rough', 'sad', 'goof']
In [16]: for word in words:
....: bins[len(word)-1].append(word)
....:
So, what is going to be in bins
now?
In [65]: bins
Out[65]:
[['one', 'three', 'rough', 'sad', 'goof'],
['one', 'three', 'rough', 'sad', 'goof'],
['one', 'three', 'rough', 'sad', 'goof'],
['one', 'three', 'rough', 'sad', 'goof'],
['one', 'three', 'rough', 'sad', 'goof']]
We multiplied a sequence containing a single mutable object.
We got a list containing five references to a single mutable object.
Watch out especially for passing mutable objects as default values for function parameters:
In [71]: def accumulator(count, list=[]):
....: for i in range(count):
....: list.append(i)
....: return list
....:
In [72]: accumulator(5)
Out[72]: [0, 1, 2, 3, 4]
In [73]: accumulator(7)
Out[73]: [0, 1, 2, 3, 4, 0, 1, 2, 3, 4, 5, 6]
Mutable Sequence Methods¶
In addition to all the methods supported by sequences we’ve seen above, mutable sequences (the List), have a number of other methods that are used to change the list.
You can find all these in the Standard Library Documentation:
https://docs.python.org/3/library/stdtypes.html#typesseq-mutable
Assignment¶
You’ve already seen changing a single element of a list by assignment.
Pretty much the same as “arrays” in most languages:
In [100]: list = [1, 2, 3]
In [101]: list[2] = 10
In [102]: list
Out[102]: [1, 2, 10]
Growing the List¶
.append()
, .insert()
, .extend()
In [74]: food = ['spam', 'eggs', 'ham']
In [75]: food.append('sushi')
In [76]: food
Out[76]: ['spam', 'eggs', 'ham', 'sushi']
In [77]: food.insert(0, 'beans')
In [78]: food
Out[78]: ['beans', 'spam', 'eggs', 'ham', 'sushi']
In [79]: food.extend(['bread', 'water'])
In [80]: food
Out[80]: ['beans', 'spam', 'eggs', 'ham', 'sushi', 'bread', 'water']
You can pass any sequence to .extend()
:
In [85]: food
Out[85]: ['beans', 'spam', 'eggs', 'ham', 'sushi', 'bread', 'water']
In [86]: food.extend('spaghetti')
In [87]: food
Out[87]:
['beans', 'spam', 'eggs', 'ham', 'sushi', 'bread', 'water',
's', 'p', 'a', 'g', 'h', 'e', 't', 't', 'i']
Shrinking the List¶
.pop()
, .remove()
In [203]: food = ['spam', 'eggs', 'ham', 'toast']
In [204]: food.pop()
Out[204]: 'toast'
In [205]: food.pop(0)
Out[205]: 'spam'
In [206]: food
Out[206]: ['eggs', 'ham']
In [207]: food.remove('ham')
In [208]: food
Out[208]: ['eggs']
You can also delete slices of a list with the del
keyword:
In [92]: nums = range(10)
In [93]: nums
Out[93]: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
In [94]: del nums[1:6:2]
In [95]: nums
Out[95]: [0, 2, 4, 6, 7, 8, 9]
In [96]: del nums[-3:]
In [97]: nums
Out[97]: [0, 2, 4, 6]
Copying Lists¶
You can make copies of part of a list using slicing:
In [227]: food = ['spam', 'eggs', 'ham', 'sushi']
In [228]: some_food = food[1:3]
In [229]: some_food[1] = 'bacon'
In [230]: food
Out[230]: ['spam', 'eggs', 'ham', 'sushi']
In [231]: some_food
Out[231]: ['eggs', 'bacon']
If you provide no arguments to the slice, it makes a copy of the entire list:
In [232]: food
Out[232]: ['spam', 'eggs', 'ham', 'sushi']
In [233]: food2 = food[:]
In [234]: food is food2
Out[234]: False
The copy of a list made this way is a shallow copy.
The list is itself a new object, but the objects it contains are not.
Mutable objects in the list can be mutated in both copies:
In [249]: food = ['spam', ['eggs', 'ham']]
In [251]: food_copy = food[:]
In [252]: food[1].pop()
Out[252]: 'ham'
In [253]: food
Out[253]: ['spam', ['eggs']]
In [256]: food.pop(0)
Out[256]: 'spam'
In [257]: food
Out[257]: [['eggs']]
In [258]: food_copy
Out[258]: ['spam', ['eggs']]
Consider this common pattern:
for x in somelist:
if should_be_removed(x):
somelist.remove(x)
This looks benign enough, but changing a list while you are iterating over it can be the cause of some pernicious bugs.
For example:
In [27]: l = list(range(10))
In [28]: l
Out[28]: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
In [29]: for item in l:
....: l.remove(item)
....:
For example:
In [27]: l = list(range(10))
In [28]: l
Out[28]: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
In [29]: for item in l:
....: l.remove(item)
....:
In [30]: l
Out[30]: [1, 3, 5, 7, 9]
Iterate over a copy, and mutate the original:
In [33]: l = list(range(10))
In [34]: for item in l[:]:
....: l.remove(item)
....:
In [35]: l
Out[35]: []
Okay, so we’ve done this a bunch already, but let’s state it out loud.
You can iterate over a sequence.
for element in sequence:
do_something(element)
which is what we mean when we say a sequence is an “iterable”.
Again, we’ll touch more on this in a short while, but first a few more words about Lists and Tuples.
Miscellaneous List Methods¶
These methods change a list in place and are not available on immutable sequence types.
.reverse()
In [129]: food = ['spam', 'eggs', 'ham']
In [130]: food.reverse()
In [131]: food
Out[131]: ['ham', 'eggs', 'spam']
.sort()
In [132]: food.sort()
In [133]: food
Out[133]: ['eggs', 'ham', 'spam']
Because these methods mutate the list in place, they have a return value of None
.sort()
can take an optional key
parameter.
It should be a function that takes one parameter (list items one at a time) and returns something that can be used for sorting:
In [137]: def third_letter(string):
.....: return string[2]
.....:
In [138]: food.sort(key=third_letter)
In [139]: food
Out[139]: ['spam', 'eggs', 'ham']
List Performance¶
- indexing is fast and constant time: O(1)
x in l
is proportional to n: O(n)- visiting all is proportional to n: O(n)
- operating on the end of list is fast and constant time: O(1)
- append(), pop()
- operating on the front (or middle) of the list depends on n: O(n)
pop(0)
,insert(0, v)
- But, reversing is fast.
Also, collections.deque
Choosing Lists or Tuples¶
Here are a few guidelines on when to choose a list or a tuple:
- If it needs to mutable: list
- If it needs to be immutable: tuple
- (safety when passing to a function)
Otherwise ... taste and convention
Convention¶
Lists are Collections (homogeneous): – contain values of the same type – simplifies iterating, sorting, etc
tuples are mixed types: – Group multiple values into one logical thing – Kind of like simple C structs.
Other Considerations¶
- Do the same operation to each element?
- list
- Small collection of values which make a single logical item?
- tuple
- To document that these values won’t change?
- tuple
- Build it iteratively?
- list
- Transform, filter, etc?
- list
More Documentation¶
For more information, read the list docs:
https://docs.python.org/3.5/library/stdtypes.html#mutable-sequence-types
(actually any mutable sequence....)
Iteration¶
Repetition, Repetition, Repetition, Repe...
For Loops¶
We’ve seen simple iteration over a sequence with for ... in
:
In [170]: for x in "a string":
.....: print(x)
.....:
a
s
t
r
i
n
g
Contrast this with other languages, where you must build and use an index
:
for(var i = 0; i < arr.length; i++) {
var value = arr[i];
alert(i + ") " + value);
If you do need an index, you can use enumerate
:
In [140]: for idx, letter in enumerate('Python'):
.....: print(idx, letter, end=' ')
.....:
0 P 1 y 2 t 3 h 4 o 5 n
range
and for
Loops¶
The range
builtin is useful for looping a known number of times:
In [171]: for i in range(5):
.....: print(i)
.....:
0
1
2
3
4
But you don’t really need to do anything at all with i
In fact, it’s a common convension to make this clear with a “nothing” name:
In [21]: for __ in range(5):
....: print("*")
....:
*
*
*
*
*
Be alert that a loop does not create a local namespace:
In [172]: x = 10
In [173]: for x in range(3):
.....: pass
.....:
In [174]: x
Out[174]: 2
Sometimes you want to interrupt or alter the flow of control through a loop.
Loops can be controlled in two ways, with break
and continue
The break
keyword will cause a loop to immediately terminate:
In [141]: for i in range(101):
.....: print(i)
.....: if i > 50:
.....: break
.....:
0 1 2 3 4 5... 46 47 48 49 50 51
The continue
keyword will skip later statements in the loop block, but
allow iteration to continue:
In [143]: for in in range(101):
.....: if i > 50:
.....: break
.....: if i < 25:
.....: continue
.....: print(i, end=' ')
.....:
25 26 27 28 29 ... 41 42 43 44 45 46 47 48 49 50
For loops can also take an optional else
block.
Executed only when the loop exits normally (not via break):
In [147]: for x in range(10):
.....: if x == 11:
.....: break
.....: else:
.....: print('finished')
finished
In [148]: for x in range(10):
.....: if x == 5:
.....: print(x)
.....: break
.....: else:
.....: print('finished')
5
This is a really nice unique Python feature!
While Loops¶
The while
keyword is for when you don’t know how many loops you need.
It continues to execute the body until condition is not True
:
while a_condition:
some_code
in_the_body
while
is more general than for
– you can always express for
as while
, but not always vice-versa.
while
is more error-prone – requires some care to terminate
loop body must make progress, so condition can become False
potential error – infinite loops:
i = 0;
while i < 5:
print(i)
Use break
:
In [150]: while True:
.....: i += 1
.....: if i > 10:
.....: break
.....: print(i)
.....:
1 2 3 4 5 6 7 8 9 10
Set a flag:
In [156]: import random
In [157]: keep_going = True
In [158]: while keep_going:
.....: num = random.choice(range(5))
.....: print(num)
.....: if num == 3:
.....: keep_going = False
.....:
3
Use a condition:
In [161]: while i < 10:
.....: i += random.choice(range(4))
.....: print(i)
.....:
0 0 2 3 4 6 8 8 8 9 12
Similarities¶
Both for
and while
loops can use break
and continue
for
internal flow control.
Both for
and while
loops can have an optional else
block
In both loops, the statements in the else
block are only executed if the
loop terminates normally (no break
)
String Features¶
Fun with Strings
Strings¶
A string literal creates a string type
(we’ve seen this already...)
"this is a string"
'So is this'
"""and this also"""
You can also use str()
In [256]: str(34)
Out[256]: '34'
String Methods¶
String objects have a lot of methods.
Here are just a few:
String Manipulations¶
split
and join
:
In [167]: csv = "comma, separated, values"
In [168]: csv.split(', ')
Out[168]: ['comma', 'separated', 'values']
In [169]: psv = '|'.join(csv.split(', '))
In [170]: psv
Out[170]: 'comma|separated|values'
Case Switching¶
In [171]: sample = 'A long string of words'
In [172]: sample.upper()
Out[172]: 'A LONG STRING OF WORDS'
In [173]: sample.lower()
Out[173]: 'a long string of words'
In [174]: sample.swapcase()
Out[174]: 'a LONG STRING OF WORDS'
In [175]: sample.title()
Out[175]: 'A Long String Of Words'
Testing¶
In [181]: number = "12345"
In [182]: number.isnumeric()
Out[182]: True
In [183]: number.isalnum()
Out[183]: True
In [184]: number.isalpha()
Out[184]: False
In [185]: fancy = "Th!$ $tr!ng h@$ $ymb0l$"
In [186]: fancy.isalnum()
Out[186]: False
String Literals¶
Common Escape Sequences:
\\ Backslash (\)
\a ASCII Bell (BEL)
\b ASCII Backspace (BS)
\n ASCII Linefeed (LF)
\r ASCII Carriage Return (CR)
\t ASCII Horizontal Tab (TAB)
\ooo Character with octal value ooo
\xhh Character with hex value hh
for example – for tab-separted values:
In [25]: s = "these\tare\tseparated\tby\ttabs"
In [26]: print(s)
these are separated by tabs
https://docs.python.org/3/reference/lexical_analysis.html#string-and-bytes-literals https://docs.python.org/3/library/stdtypes.html#string-methods
Raw Strings¶
Add an r
in front of the string literal:
Escape Sequences Ignored
In [408]: print("this\nthat")
this
that
In [409]: print(r"this\nthat")
this\nthat
Gotcha
In [415]: r"\"
SyntaxError: EOL while scanning string literal
(handy for regex, windows paths...)
Ordinal values¶
Characters in strings are stored as numeric values:
- “ASCII” values: 1-127
- Unicode values – 1 - 1,114,111 (!!!)
To get the value:
In [109]: for i in 'Chris':
.....: print(ord(i), end=' ')
67 104 114 105 115
In [110]: for i in (67,104,114,105,115):
.....: print(chr(i), end='')
Chris
(these days, stick with ASCII, or use full Unicode: more on that in a few weeks)
Building Strings¶
You can, but please don’t do this:
'Hello ' + name + '!'
(I know – we did that in the grid_printing excercise)
Do this instead:
'Hello {}!'.format(name)
It’s much faster and safer, and easier to modify as code gets complicated.
https://docs.python.org/3/library/string.html#string-formatting
Old and New string formatting¶
back in early python days, there was the string formatting operator: %
" a string: %s and a number: %i "%("text", 45)
This is very similar to C-style string formatting (sprintf).
It’s still around, and handy — but ...
The “new” format()
method is more powerful and flexible, so we’ll focus on that in this class.
The string format()
method:
In [62]: "A decimal integer is: {:d}".format(34)
Out[62]: 'A decimal integer is: 34'
In [63]: "a floating point is: {:f}".format(34.5)
Out[63]: 'a floating point is: 34.500000'
In [64]: "a string is the default: {}".format("anything")
Out[64]: 'a string is the default: anything'
Multiple placeholders:¶
In [65]: "the number is {} is {}".format('five', 5)
Out[65]: 'the number is five is 5'
In [66]: "the first 3 numbers are {}, {}, {}".format(1,2,3)
Out[66]: 'the first 3 numbers are 1, 2, 3'
The counts must agree:
In [67]: "string with {} formatting {}".format(1)
---------------------------------------------------------------------------
IndexError Traceback (most recent call last)
<ipython-input-67-a079bc472aca> in <module>()
----> 1 "string with {} formatting {}".format(1)
IndexError: tuple index out of range
Named placeholders:¶
In [69]: "Hello, {name}, whaddaya know?".format(name="Joe")
Out[69]: 'Hello, Joe, whaddaya know?'
You can use values more than once, and skip values:
In [73]: "Hi, {name}. Howzit, {name}?".format(name='Bob')
Out[73]: 'Hi, Bob. Howzit, Bob?'
The format operator works with string variables, too:
In [80]: s = "{:d} / {:d} = {:f}"
In [81]: a, b = 12, 3
In [82]: s.format(a, b, a/b)
Out[82]: '12 / 3 = 4.000000'
So you can dynamically build a format string
Complex Formatting¶
There is a complete syntax for specifying all sorts of options.
It’s well worth your while to spend some time getting to know this formatting language. You can accomplish a great deal just with this.
One Last Trick¶
For some of the exercises, you’ll need to interact with a user at the command line.
There’s a nice built in function to do this - input
:
In [85]: fred = input('type something-->')
type something-->I've typed something
In [86]: print(fred)
I've typed something
This will display a prompt to the user, allowing them to input text and allowing you to bind that input to a symbol.