More Python
Tuples
Similar to lists, but immutable.
>>> t = ('a', 'b', 'c')
>>> for elem in t:
... print elem
a
b
c
>>> print t[1]
b
>>> t.sort()
...
AttributeError: 'tuple' object has no attribute 'sort'
Please ignore what I just said.
Despite the surface similarities, tuples have a very different purpose from lists.
A list is for a variable-size collection of
similar things.
A tuple is generally for describing multiple
aspects of a single thing, where each spot in the tuple has
an (implicit) meaning. For instance, a tuple about me: ('Carl',
'Meyer', 29, 74, 165).
But if we've got a group of people, use a list (of tuples!):
[('Carl', 'Meyer', 29, 74, 165),
('Fred', 'Flintstone', 43, 65, 257)]
Summary
Tuples are fine for simple data structures, where you can easily keep in your head what field means what. Later we'll cover classes, a more explicit way to define compound data structures.
String formatting
Last week we saw simple string concatenation: print 'Your name is ' + first_name + ' ' + last_name. This breaks if we try to concatenate something that isn't a string:
>>> age = 29 >>> print 'You are ' + age + ' years old. ... TypeError: cannot concatenate 'str' and 'int' objects
Instead...
In most cases, you're better off with string formatting:
people = [('Carl', 'Meyer', 29, 74, 165),
('Fred', 'Flintstone', 43, 65, 257)]
for person in people:
fn, ln, age, height, weight = person
print '%s %s is %s years old, %s inches tall, and weighs %s.' % (fn, ln, age, height, weight)
Or...
people = [('Carl', 'Meyer', 29, 74, 165),
('Fred', 'Flintstone', 43, 65, 257)]
for person in people:
print '%s %s is %s years old, %s inches tall, and weighs %s.' % person
Splitting strings
Say we're working with file paths, so we have a variable path = '/Volumes/HOMES/Classes/COMM385'. We want to know what the last bit of that path is. So we split the string:
>>> bits = path.split('/')
>>> bits
['', 'Volumes', 'HOMES', 'Classes', 'COMM385']
>>> bits[-1]
'COMM385'
Joining lists
Now we'll change the last bit of the path, and then put it back together into a single string:
>>> bits[-1] = 'CS101' >>> bits ['', 'Volumes', 'HOMES', 'Classes', 'CS101'] >>> '/'.join(bits) '/Volumes/HOMES/Classes/CS101'
More on Functions
Functions can have multiple arguments (of course), and some of them can be optional:
def first(a_list, num=1):
"""
Return the first X elements in a list (one element by default).
"""
return a_list[:num]
So we can call this function with either one or two arguments:
>>> my_list = [0, 1, 2, 3] >>> first(my_list) [0] >>> first(my_list, 3) [0, 1, 2]
Using named arguments
If a function has multiple optional arguments, we can specify exactly which ones we want to override when we call it:
def html_tag(contents, tag='p', attributes=None):
"Create an HTML tag with contents."
if attributes is None:
attrs_string = ''
else:
attrs_string = ' '.join(['%s="%s"' % (k,v)
for k,v
in attributes.items()])
return "<%s%s>%s</%s>" % (tag, attrs_string, contents, tag)
>>> html_tag('Some stuff', attributes={'id': 'stuff'})
'<p id="stuff">Some stuff</p>'
I got better
We can allow a function to take an arbitrary number of "keyword arguments":
def html_tag(contents, tag='p', **attributes):
"Create an HTML tag with contents."
print attributes
if not attributes:
attrs_string = ''
else:
attrs_string = ' '.join(['%s="%s"' % (k,v)
for k,v
in attributes.items()])
return "<%s%s>%s</%s>" % (tag, attrs_string, contents, tag)
>>> html_tag('Some stuff', tag='div', id='stuff', class='aside')
{'id': 'stuff', 'class': 'aside'}
'<div id="stuff" class="aside">Some stuff</div>'
Help! I'm being oppressed!
With a single asterisk, your function can collect an arbitrary number of non-keyword arguments:
def sum(*args):
"Return sum of all arguments."
total = 0
for arg in args:
total += arg
return total
>>> sum(1, 2, 3)
6
>>> sum(2, 4, 6, 8, 10)
30
Come see the violence inherent in the system
And, of course, you can have both *args and **kwargs in the same function:
def print_args(*args, **kwargs):
"Print out all arguments."
print args
print kwargs
>>> print_args(1, 2, 3, foo="bar", baz="quux")
[1, 2, 3]
{'foo': 'bar', 'baz': 'quux'}
One more functional brain-bender
In Python, functions are first-class values. That means we can refer to them, pass them around, assign them to variables, just like we can with numbers, strings, etc. We can even write functions that take other functions as arguments:
def apply_twice(func, argument):
"""
Applies a function to one argument, and then applies the
same function again to the result.
"""
return func(func(argument))
>>> def double(x): return x*2
...
>>> double(3)
6
>>> apply_twice(double, 5)
I left out the result of that last function call - what will it be?
Reading
Chapter 4 in Dive Into Python
Homework
- Write a function
css_generatorthat takes as arguments a selector (i.e.".wildtype") and then an arbitrary number of attribute-value pairs (hint: use**kwargs), and returns a string that is a valid CSS block. (If returning a large multi-line string, it's often handy to build it up as a list of strings, each element one line, and then at the endreturn "\n".join(list_of_lines).) For instance, if I callcss_generator(".wildtype", color="#66CC00"), the return value should be'.wildtype {\ncolor: #66CC00;\n}' - Write a function
mapthat takes two arguments: a function of one argument, and a list. Return the result of applying the given function to every element of the list. For instance, if I havedef double(x): return 2*xand I callmap(double, [2, 4, 3]), the result should be[4, 8, 6].
...and test it
For each of these functions, demonstrate that they work by writing
test code that uses them. Use the if __name__ ==
'__main__': trick discussed in Dive Into Python
section 2.6 so that your test code runs if you run python
my_file.py, but does not run if you import your module.