Difference between revisions of "Python and MySQL"

From PeformIQ Upgrade
Jump to navigation Jump to search
(New page: Check this out in [HTML format|http://esbinfo:8090/download/attachments/1704632/python-and-mysql.html] ... {html} <DIV class="layout"> <DIV id="currentSlide"></DIV> <DIV id="header"></DI...)
 
Line 1: Line 1:
Check this out in [HTML format|http://esbinfo:8090/download/attachments/1704632/python-and-mysql.html] ...
Check this out in [HTML format|http://esbinfo:8090/download/attachments/1704632/python-and-mysql.html] ... {html}


{html}
<div class="layout"><div id="currentSlide"></div><div id="header"></div><div id="footer">
<DIV class="layout">


<DIV id="currentSlide"></DIV>
= MySQL Users Conference 2005, Santa Clara, CA =
<DIV id="header"></DIV>
<DIV id="footer">
<H1>MySQL Users Conference 2005, Santa Clara, CA</H1>
<H2>Python and MySQL</H2>
<DIV id="controls"></DIV>
</DIV>


</DIV>
== Python and MySQL ==


<div id="controls"></div></div></div><div class="presentation"><div class="slide">


<DIV class="presentation">
= Python and MySQL =


<DIV class="slide">
=== Andy Dustman ===
<H1>Python and MySQL</H1>
<H3>Andy Dustman</H3>
<H4>Office of Information Technology</H4>
<H4>Terry College of Business</H4>
<H4>University of Georgia</H4>
</DIV>
<DIV class="handout">
<DL>
<DT>Andy Dustman</DT>
<DD><A href="mailto:adustman%40terry.uga.edu"></A><ADUSTMAN><A href="mailto:adustman%40terry.uga.edu"></A></ADUSTMAN><BR></BR></DD>
<DT>Terry College of Business</DT>
<DD><A href="http://www.terry.uga.edu/">http://www.terry.uga.edu/</A></DD>
<DT>University of Georgia</DT>
<DD><A href="http://www.uga.edu/">http://www.uga.edu/</A></DD>
</DL>
</DIV>


<DIV class="slide">
==== Office of Information Technology ====
<H1>Python for the PyCurious</H1>
<UL>
<LI>interpreted (byte-code compiler)</LI>
<LI>interactive (easy to test ideas)</LI>
<LI>object-oriented (everything's an object)</LI>
<LI>rapid development (5-10x C++, Java)</LI>
<LI>fits your brain <I>[Bruce Eckel]</I></LI>
<LI>fits your wallet: free (OSI and GPL)</LI>
<LI>fun!</LI>
</UL>
<DIV class="handout">
<DL>
<DT>Introductory Material on Python:</DT>
<DD><A href="http://www.python.org/doc/Intros.html">http://www.python.org/doc/Intros.html</A></DD></DL>
</DIV>
</DIV>


<DIV class="slide">
==== Terry College of Business ====
<H1>Types</H1>
<DIV id="typecomparo">
<TABLE>
<THEAD>
<TR>
<TH> <BR></BR></TH><TH scope="col">Mutable</TH> <TH scope="col">Immutable</TH></TR></THEAD>
<TBODY>
<TR><TH scope="row">Sequence</TH><TD>list</TD> <TD>tuple<BR></BR>str, unicode</TD></TR>
<TR><TH scope="row">Number</TH><TD> <BR></BR></TD> <TD>int, long, float</TD></TR>
<TR><TH scope="row">Mapping</TH><TD>dict</TD> <TD> <BR></BR></TD></TR>
<TR><TH scope="row">Other</TH><TD>object</TD> <TD> <BR></BR></TD></TR>
</TBODY>
<CAPTION>The basic Python types and their mutability</CAPTION>
</TABLE>
</DIV>
</DIV>


<DIV class="slide">
==== University of Georgia ====
<H1>Basic type examples</H1>
<PRE class="interactive">>>> i=1 # an int<BR></BR>>>> j=2**64-1 # a long integer<BR></BR>>>> print j<BR></BR>18446744073709551615<BR></BR>>>> f=3.14 # float (C double)<BR></BR>>>> c=1-1j # complex (1j is imaginary)<BR></BR>>>> print c<BR></BR>(1-1j)<BR></BR>>>> s="welcome to python!"<BR></BR>>>> s.capitalize().split() # returns a list<BR></BR>['Welcome', 'to', 'python!']<BR></BR>>>> [ word.capitalize() for word in s.split() ]<BR></BR>['Welcome', 'To', 'Python!']<BR></BR>>>> a, b = 1, 2<BR></BR>>>> print (a,b) # a tuple<BR></BR>(1, 2)<BR></BR>>>> a, b = b, a<BR></BR>>>> print (a,b)<BR></BR>(2, 1)<BR></BR></PRE>
</DIV>


<DIV class="slide">
</div><div class="handout">
<H1>Strings</H1>
<PRE class="interactive">>>> "Normal string literal isn't very interesting."<BR></BR>"Normal string literal isn't very interesting."<BR></BR>>>> 'Single quotes work "same as double".'<BR></BR>'Single quotes work "same as double".'<BR></BR>>>> """Triple-quoted strings are good for long strings<BR></BR>... which span multiple lines."""<BR></BR>'Triple-quoted strings are good for long strings\nwhich span multiple lines.'<BR></BR>>>> r"Raw strings are useful for regexs, i.e. \w+ or \1"<BR></BR>'Raw strings are useful for regexs, i.e. \\w+ or \\1'<BR></BR>>>> u"Unicode strings work just like regular strings."<BR></BR>u'Unicode strings work just like regular strings.'<BR></BR>>>> u"\u72c2\n\u7009".encode('utf-8')<BR></BR>'\xe7\x8b\x82\n\xe7\x80\x89'<BR></BR>>>> print u"\u72c2\n\u7009".encode('utf-8')<BR></BR>¿<BR></BR>¿<BR></BR></PRE>
</DIV>


<DIV class="slide">
; Andy Dustman
<H1>Strings</H1>
: [mailto:adustman%40terry.uga.edu ][mailto:adustman%40terry.uga.edu ]<br />
<P>Lots of string methods and operators:</P>
; Terry College of Business
<PRE class="interactive">>>> "Split words into a list.".split()<BR></BR>['Split', 'words', 'into', 'a', 'list.']<BR></BR>>>> ' '.join(['Join', 'a', 'list', 'of', 'strings'])<BR></BR>'Join a list of strings'<BR></BR>>>> "Concatenate" + " " + "strings"<BR></BR>'Concatenate strings'<BR></BR>>>> "Multiplicity! " * 3<BR></BR>'Multiplicity! Multiplicity! Multiplicity! '<BR></BR>>>> "Parameter %s" % "substitution"<BR></BR>'Parameter substitution'<BR></BR>>>> d = dict(first_name="Vee", last_name="McMillen",<BR></BR>... company="O'Reilly")<BR></BR>>>> "Hello, %(first_name)s. How are things at %(company)s?" % d<BR></BR>"Hello, Vee. How are things at O'Reilly?"<BR></BR></PRE>
: http://www.terry.uga.edu/
</DIV>
; University of Georgia
: http://www.uga.edu/


<DIV class="slide">
</div><div class="slide">
<H1>Dictionaries</H1>
<P>Python dictionaries are like perl hashes:</P>
<PRE class="interactive">>>> d1={}<BR></BR>>>> d1['a']=1<BR></BR>>>> d1['b']=2<BR></BR>>>> d1['c']=3<BR></BR>>>> d1<BR></BR>{'a': 1, 'c': 3, 'b': 2}<BR></BR>>>> d2={'a': 1, 'c': 3, 'b': 2}<BR></BR>>>> d3=dict([('a',1),('b',2),('c',3)])<BR></BR>>>> d4=dict(a=1, b=2, c=3)<BR></BR>>>> d1 == d2 == d3 == d4<BR></BR>True<BR></BR>>>> len(d1)<BR></BR>3<BR></BR></PRE>
<P>Values can be any type, but keys must be immutable.</P>
</DIV>


<DIV class="slide">
= Python for the PyCurious =
<H1>Sequences</H1>
<PRE class="interactive">>>> l = ['a','b','c','d','e']<BR></BR>>>> print l[0]<BR></BR>a<BR></BR>>>> print l[-1]<BR></BR>e<BR></BR>>>> print l[2:4]<BR></BR>['c', 'd']<BR></BR>>>> s='abcde'<BR></BR>>>> print s[2:4]<BR></BR>cd<BR></BR>>>> print s[::2]<BR></BR>ace<BR></BR>>>> print s[::-1]<BR></BR>edcba<BR></BR>>>> l.append(s)<BR></BR>>>> print l<BR></BR>['a', 'b', 'c', 'd', 'e', 'abcde']<BR></BR></PRE>
</DIV>


<DIV class="slide">
* interpreted (byte-code compiler)
<H1>Iterators</H1>
* interactive (easy to test ideas)
<UL>
* object-oriented (everything's an object)
<LI><CODE>iter(object)</CODE> returns an iterator object</LI>
* rapid development (5-10x C++, Java)
<LI><CODE>iterobj.next()</CODE> returns the next object</LI>
* fits your brain <i>[Bruce Eckel]</i>
<LI><CODE>StopIteration</CODE> is raised when there are no more objects
* fits your wallet: free (OSI and GPL)
<PRE class="interactive">>>> # no normal person would do this<BR></BR>>>> l = [1, 2, 3]<BR></BR>>>> i = iter(l)<BR></BR>>>> i.next()<BR></BR>1<BR></BR>>>> i.next()<BR></BR>2<BR></BR>>>> i.next()<BR></BR>3<BR></BR>>>> i.next()<BR></BR>Traceback (most recent call last):<BR></BR>  File "<STDIN>", line 1, in ?<BR></BR>StopIteration<BR></BR></STDIN></PRE></LI>
* fun!
</UL>


</DIV>
<div class="handout">


<DIV class="slide">
; Introductory Material on Python:
<H1>Common iterator usage</H1>
: http://www.python.org/doc/Intros.html
<PRE class="interactive">>>> l = [1, 2, 3]<BR></BR>>>> for item in l:<BR></BR>...    print item<BR></BR>...<BR></BR>1<BR></BR>2<BR></BR>3<BR></BR>>>> d = dict(a=1, b=2, c=3)<BR></BR>>>> for key in d:<BR></BR>...    print key, d[key]<BR></BR>...<BR></BR>a 1<BR></BR>c 3<BR></BR>b 2<BR></BR></PRE>
</DIV>


<DIV class="slide">
</div></div><div class="slide">
<H1>Exceptions</H1>
<PRE>f = open("myfile", 'r')<BR></BR>try:<BR></BR>   try:<BR></BR>        for line in f:<BR></BR>            try:<BR></BR>                process(line)<BR></BR>            except TypeError:<BR></BR>                line = mangle(line)<BR></BR>                try:<BR></BR>                    process(line)<BR></BR>                except TypeError:<BR></BR>                    raise FoobarError, line<BR></BR>    except IOError, message:<BR></BR>        print "Error reading:", message<BR></BR>    except FoobarError:<BR></BR>        print "This file is totally munged."<BR></BR>    except:<BR></BR>        print "Something inexplicable happened:"<BR></BR>        raise # re-raise original exception<BR></BR>finally:<BR></BR>    f.close()<BR></BR></PRE>
</DIV>


<DIV class="slide">
= Types =
<H1>Odds and ends</H1>
<UL>
<LI>Code blocks are delimited by indentation
<UL>
<LI>You probably do this already</LI>
<LI>Space or tabs, your call; just be consistent</LI>
<LI>No need for curly braces</LI>
<LI>Less cluttered, easier to read</LI>
</UL></LI>
<LI>End-of-line is a statement separator (so is <CODE>;</CODE>)</LI>
<LI>No type enforcement<UL>
<LI>Argument types are not checked</LI>
<LI>Function return types are not checked</LI>
<LI>Type checking makes your code less flexible</LI>
<LI>If you still want it, you can add it cleanly with decorators</LI></UL>
</LI><LI>Operator overloading for user-defined classes</LI>
<LI><I>Everything</I> is a reference (pass by reference)</LI>
<LI><CODE>None</CODE> object for null/missing values (equivalent to <CODE>NULL</CODE>)</LI>
</UL>
</DIV>


<DIV class="slide">
<div id="typecomparo">
<H1>Odds and ends</H1>
<UL>
<LI>Member access with <CODE>.</CODE> operator<UL>
<LI><CODE>instance.method()</CODE></LI>
<LI><CODE>instance.attribute</CODE></LI>
<LI><CODE>instance.attribute.another</CODE></LI></UL>
</LI><LI>Functions/methods are not the only things that are callable</LI>
<LI>Decorators apply a callable to a function at creation time:<PRE>@g<BR></BR>def f(x):<BR></BR>    ...<BR></BR></PRE> is equivalent to:<PRE>def f(x):<BR></BR>    ...<BR></BR>f = g(f)</PRE></LI>
</UL>
</DIV>


<DIV class="slide">
{|
<H1>The Python DB-API</H1>
! <br />
<UL>
! scope="col" | Mutable
<LI>Standard API for database access</LI>
! scope="col" | Immutable
<LI>PEP 249: <A href="http://www.python.org/peps/pep-0249.html" title="PEP 249: Python Database API v2.0">http://www.python.org/peps/pep-0249.html</A></LI>
! scope="row" | Sequence
<LI>By convention, module name ends with "db", i.e. MySQLdb</LI>
| list
<UL>
| tuple<br />str, unicode
<LI>Module Interface</LI>
|-
<LI>Connection Objects</LI>
! scope="row" | Number
<LI>Cursor Objects</LI>
| <br />
<LI>DBI Helper Objects</LI>
| int, long, float
<LI>Type Objects and Constructors</LI>
|-
<LI>Implementation Hints</LI>
! scope="row" | Mapping
<LI>Major Changes from 1.0 to 2.0</LI>
| dict
</UL>
| <br />
</UL></DIV>
|-
! scope="row" | Other
| object
| <br />
|+ The basic Python types and their mutability
|}


<DIV class="slide">
</div></div><div class="slide">
<H1>Module Interface</H1>
<DL>
<DT>connect(...)</DT>
<DD>Constructor for creating a connection to the database.
            Returns a Connection Object.</DD>
<DT>apilevel</DT>
<DD>String constant stating the supported DB API level.</DD>
<DT>threadsafety</DT>
<DD> Integer constant stating the level of thread safety the
interface supports.</DD>
</DL>
</DIV>


<DIV class="slide">
= Basic type examples =
<H1>SQL parameter placeholders</H1>
<DT>paramstyle</DT>
<DD>String constant stating the type of parameter marker formatting expected by the interface.
<DL>
<DT>'qmark'</DT>
<DD>Question mark style, e.g. '...WHERE name=?'</DD>
<DT>'numeric'</DT>
<DD>Numeric, positional style, e.g. '...WHERE name=:1'</DD>
<DT>'named'</DT>
<DD>Named style, e.g. '...WHERE name=:name'</DD>
<DT>'format'</DT>
<DD>ANSI C printf format codes, e.g. '...WHERE name=%s'</DD>
<DT>'pyformat'</DT>
<DD>Python extended format codes, e.g. '...WHERE name=%(name)s'</DD>


MySQLdb 1.0 and 1.2 uses format and pyformat; 2.0 may also support qmark.
&gt;&gt;&gt; i=1 # an int<br />&gt;&gt;&gt; j=2**64-1 # a long integer<br />&gt;&gt;&gt; print j<br />18446744073709551615<br />&gt;&gt;&gt; f=3.14 # float (C double)<br />&gt;&gt;&gt; c=1-1j # complex (1j is imaginary)<br />&gt;&gt;&gt; print c<br />(1-1j)<br />&gt;&gt;&gt; s="welcome to python!"<br />&gt;&gt;&gt; s.capitalize().split() # returns a list<br />['Welcome', 'to', 'python!']<br />&gt;&gt;&gt; [ word.capitalize() for word in s.split() ]<br />['Welcome', 'To', 'Python!']<br />&gt;&gt;&gt; a, b = 1, 2<br />&gt;&gt;&gt; print (a,b) # a tuple<br />(1, 2)<br />&gt;&gt;&gt; a, b = b, a<br />&gt;&gt;&gt; print (a,b)<br />(2, 1)<br />
</DL>
</DD></DIV>


<DIV class="slide">
</div><div class="slide">
<H1>Exceptions</H1>
<UL>
<LI>StandardError
<UL>
<LI>Warning</LI>
<LI>Error
<UL>
<LI>InterfaceError</LI>
<LI>DatabaseError</LI>
<LI>DataError</LI>
<LI>OperationalError</LI>
<LI>IntegrityError</LI>
<LI>InternalError</LI>
<LI>ProgrammingError</LI>
<LI>NotSupportedError</LI></UL>
</LI></UL>
</LI>
</UL>
</DIV>


<DIV class="slide">
= Strings =
<H1>Connection Object</H1>
<DL>
<DT>.close()</DT>
<DD>Close the connection now</DD>
<DT>.commit()</DT>
<DD>Commit any pending transaction to the database. Auto-commit off by default.</DD>
<DT>.rollback()</DT>
<DD>Rollback any pending transaction.</DD>
<DT>.cursor()</DT>
<DD>Return a new Cursor Object using the connection.</DD>
<DT><I>exceptions</I></DT>
<DD>The standard exception classes; simplfies error handling in some cases</DD>
<DT>.messages</DT>
<DD>list of error/warning messages since last method call</DD>
</DL>
</DIV>


<DIV class="slide">
&gt;&gt;&gt; "Normal string literal isn't very interesting."<br />"Normal string literal isn't very interesting."<br />&gt;&gt;&gt; 'Single quotes work "same as double".'<br />'Single quotes work "same as double".'<br />&gt;&gt;&gt; """Triple-quoted strings are good for long strings<br />... which span multiple lines."""<br />'Triple-quoted strings are good for long strings
<H1>Cursor Object</H1>
which span multiple lines.'<br />&gt;&gt;&gt; r"Raw strings are useful for regexs, i.e. w+ or �"<br />'Raw strings are useful for regexs, i.e. \w+ or \1'<br />&gt;&gt;&gt; u"Unicode strings work just like regular strings."<br />u'Unicode strings work just like regular strings.'<br />&gt;&gt;&gt; u"72c2
<DL>
7009".encode('utf-8')<br />'狂
<DT>.description</DT>
  瀉'<br />&gt;&gt;&gt; print u"72c2
<DD>A sequence of sequences, each of which describe a column of the result.</DD>
7009".encode('utf-8')<br /><br /><br />
<DT>.rowcount</DT>
<DD>Number of rows affected by last query.</DD>
<DT>.callproc(procname[,parameters])</DT>
<DD>Call a stored database procedure with the given name.</DD>
<DT>.close()</DT>
<DD>Close the cursor now.</DD>
<DT>.execute(operation[,parameters])</DT>
<DD>Prepare and execute a database operation (query or
command). Parameters: sequence or mapping.</DD>
<DT>.executemany(operation,seq_of_parameters)</DT>
<DD>Prepare a database operation (query or command) and then
execute it against a sequence of parameters.</DD>
</DL>
</DIV>


<DIV class="slide">
</div><div class="slide">
<H1>Cursor Object</H1>
<DL>
<DT>.fetchone()</DT>
<DD>Fetch the next row of the result set
as a sequence, or <I>None</I> if there are no more rows.
</DD>
<DT>.fetchmany(<I>[size=cursor.arraysize]</I>)</DT>
<DD>Fetch a sequence of up to <I>size</I> rows;
may be fewer. Zero length sequence indicates end of result set.
</DD>
<DT>.fetchall()</DT>
<DD>Fetch all remaining rows as a sequence of rows.</DD>
<DT>.nextset()</DT>
<DD>Skip to the next result set. Returns a true value if there is another
result set; <I>None</I> (false) if not.</DD>
<DT>.arraysize</DT>
<DD>Default number of rows to return with cursor.fetchmany().
Default: 1.</DD>
</DL>
</DIV>


<DIV class="slide">
= Strings =
<H1>Cursor Object</H1>
<DL>
<DT>.rownumber</DT>
<DD>Current index into result set</DD>
<DT>.connection</DT>
<DD>The Connection object for this cursor</DD>
<DT>.scroll(value<I>[,mode='relative']</I>)</DT>
<DD>Scroll to a new position in the result set (relative or absolute).</DD>
<DT>.messages</DT>
<DD>List containing warning/error messages since last method call
(except the .fetch<I>XXX</I>() methods).</DD>
<DT>.next()</DT>
<DD>Fetches one row (like fetchone()) or raises <CODE>StopIteration</CODE>
if no rows left. <I>Iterator protocol</I></DD>
<DT>.lastrowid</DT>
<DD>Row id of the last affected row (i.e. inserting <CODE>AUTO_INCREMENT</CODE>
columns)</DD>
</DL>
</DIV>


<DIV class="slide">
Lots of string methods and operators:
<H1>MySQL for Python</H1>
<UL>
<LI>MySQL-python project on SourceForge:
<A href="http://sourceforge.net/projects/mysql-python" title="MySQL-python project on SourceForge">http://sourceforge.net/projects/mysql-python</A></LI>
<LI>Current best version: 1.2.0<UL>
<LI>Python-2.3 and newer (and maybe 2.2)</LI>
<LI>MySQL-3.23, 4.0, and 4.1 (and maybe 5.0)</LI>
<LI>Prepared statements not supported yet</LI></UL></LI>
<LI>Older version: 1.0.1<UL>
<LI>Python-1.5.2 (very old) and newer</LI>
<LI>MySQL-3.22, 3.23, and 4.0 (not 4.1 or newer)</LI>
<LI>Don't use if you can use 1.2.0</LI></UL></LI>
<LI>Vaporware version: 2.0<UL>
<LI>Python-2.4 and newer</LI>
<LI>MySQL-4.0, 4.1, and 5.0</LI>
<LI>Prepared statements will be supported</LI>
<LI>Return all text columns as <CODE>unicode</CODE> by default</LI></UL></LI>
</UL>
</DIV>


<DIV class="slide">
&gt;&gt;&gt; "Split words into a list.".split()<br />['Split', 'words', 'into', 'a', 'list.']<br />&gt;&gt;&gt; ' '.join(['Join', 'a', 'list', 'of', 'strings'])<br />'Join a list of strings'<br />&gt;&gt;&gt; "Concatenate" + " " + "strings"<br />'Concatenate strings'<br />&gt;&gt;&gt; "Multiplicity! " * 3<br />'Multiplicity! Multiplicity! Multiplicity! '<br />&gt;&gt;&gt; "Parameter %s" % "substitution"<br />'Parameter substitution'<br />&gt;&gt;&gt; d = dict(first_name="Vee", last_name="McMillen",<br />... company="O'Reilly")<br />&gt;&gt;&gt; "Hello, %(first_name)s. How are things at %(company)s?" % d<br />"Hello, Vee. How are things at O'Reilly?"<br />
<H1>Architecture</H1>
<DIV class="twocol">
<H2><CODE>_mysql</CODE></H2>
<UL>
<LI>C extension module</LI>
<LI>transliteration of MySQL C API into Python objects</LI>
<LI>If you use the C API, this should be very familiar</LI>
<LI>Deprecated API calls not implemented</LI>
<LI>Not everything (particularly fields) is exposed</LI>
<LI>SQL column type to Python type conversions handled by
a dictionary</LI>
</UL>
</DIV>
<DIV class="twocol">
<H2><CODE>MySQLdb</CODE></H2>
<UL>
<LI>Adapts <CODE>_mysql</CODE> to DB-API</LI>
<LI>Many non-standard C API calls are exposed</LI>
<LI>Relatively light-weight wrapper</LI>
<LI>Implements cursors</LI>
<LI>Defines default type mappings; easily
customizable</LI>
</UL>
</DIV>
</DIV>


<DIV class="slide">
</div><div class="slide">
<H1>Opening a connection</H1>
<P><CODE>connect()</CODE> takes the same options as <CODE>mysql_real_connect()</CODE>, and then some.</P>
<PRE>import MySQLdb<BR></BR><BR></BR># These are all equivalent, for the most part<BR></BR>db = MySQLdb.connect("myhost", "myuser", "mysecret", "mydb")<BR></BR>db = MySQLdb.connect(host="myhost", user="myuser",<BR></BR>                    passwd="mysecret", db="mydb")<BR></BR>auth = dict(user="myuser", passwd="mysecret")<BR></BR>db = MySQLdb.connect("myhost", db="mydb", **auth)<BR></BR>db = MySQLdb.connect(read_default_file="/etc/mysql/myapp.cnf")<BR></BR></PRE>
<UL>
<LI><CODE>compress=1</CODE> enables gzip compression</LI>
<LI><CODE>use_unicode=1</CODE> returns text-like columns as <CODE>unicode</CODE> objects</LI>
<LI><CODE>ssl=dict(...)</CODE> negotiates SSL/TLS</LI>
</UL>
</DIV>


<DIV class="slide">
= Dictionaries =
<H1>Simple query example</H1>
<PRE>import MySQLdb<BR></BR><BR></BR>db = MySQLdb.connect(read_default_file="/etc/mysql/myapp.cnf")<BR></BR>c = db.cursor()<BR></BR>c.execute("""SELECT * FROM users WHERE userid=%s""", ('monty',))<BR></BR>print c.fetchone()<BR></BR></PRE>
<H2>Notes</H2>
<UL>
<LI><CODE>('monty',)</CODE> is a 1-tuple; comma required to distinquish
from algebraic grouping</LI>
<LI>Good reasons not to use <CODE>*</CODE><UL>
<LI>How many columns are being returned?</LI>
<LI>What is the order of the columns?</LI></UL></LI>
<LI>Good reasons to use <CODE>*</CODE><UL>
<LI>Table/database browser</LI>
<LI>Lazy</LI></UL></LI></UL>
</DIV>


<DIV class="slide">
Python dictionaries are like perl hashes:
<H1>Multi-row query example</H1>
<PRE>c = db.cursor()<BR></BR>c.execute("""SELECT userid, first_name, last_name, company<BR></BR>FROM users WHERE status=%s and expire>%s""",<BR></BR>(status, today))<BR></BR>users = c.fetchall()<BR></BR></PRE>
<H2>Notes</H2>
<UL>
<LI>We know what the columns are</LI>
<LI>Could use some object abstraction</LI>
</UL>
</DIV>


<DIV class="slide">
&gt;&gt;&gt; d1={}<br />&gt;&gt;&gt; d1['a']=1<br />&gt;&gt;&gt; d1['b']=2<br />&gt;&gt;&gt; d1['c']=3<br />&gt;&gt;&gt; d1<br />{'a': 1, 'c': 3, 'b': 2}<br />&gt;&gt;&gt; d2={'a': 1, 'c': 3, 'b': 2}<br />&gt;&gt;&gt; d3=dict([('a',1),('b',2),('c',3)])<br />&gt;&gt;&gt; d4=dict(a=1, b=2, c=3)<br />&gt;&gt;&gt; d1 == d2 == d3 == d4<br />True<br />&gt;&gt;&gt; len(d1)<br />3<br />
<H1>A simple User class</H1>
<PRE>class User(object):<BR></BR><BR></BR>    """A simple User class"""<BR></BR><BR></BR>   def __init__(self, userid,<BR></BR>                 first_name=None, last_name=None,<BR></BR>                 company=None):<BR></BR>        self.userid = userid<BR></BR>        self.first_name = first_name<BR></BR>        self.last_name = last_name<BR></BR>        self.company = company<BR></BR><BR></BR>    def announce(self):<BR></BR>        """Announce User to the world."""<BR></BR>        name = "%s %s" % (self.first_name, self.last_name)<BR></BR>       if self.company:<BR></BR>            return "%s of %s" % (name, self.company)<BR></BR>        else:<BR></BR>            return name<BR></BR><BR></BR>    def __str__(self):<BR></BR>       return self.announce()<BR></BR></PRE>
</DIV>


<DIV class="slide">
Values can be any type, but keys must be immutable.
<H1>Multi-row query with User object</H1>
<PRE>users = []<BR></BR>c = db.cursor()<BR></BR>c.execute("""SELECT userid, first_name, last_name, company<BR></BR>FROM users WHERE status=%s and expire>%s""",<BR></BR>(status, today))<BR></BR><BR></BR>for userid, first_name, last_name, company in c.fetchall():<BR></BR>    u = User(userid, first_name, last_name, company)<BR></BR>    print u<BR></BR>    users.append(u)<BR></BR></PRE>
<P>might produce output like:
</P><PRE>Tim O'Reilly of O'Reilly Media, Inc.<BR></BR>Monty Widenius of MySQL AB<BR></BR>Carleton Fiorina<BR></BR>Guido van Rossum of Elemental Security<BR></BR></PRE>
</DIV>


<DIV class="slide">
</div><div class="slide">
<H1>Cursors are iterators</H1>
<P>Not necessary to use <CODE>c.fetchall()</CODE>
</P><PRE>users = []<BR></BR>c = db.cursor()<BR></BR>c.execute("""SELECT userid, first_name, last_name, company<BR></BR>FROM users WHERE status=%s and expire>%s""",<BR></BR>(status, today))<BR></BR><BR></BR>for userid, first_name, last_name, company in c:<BR></BR>    u = User(userid, first_name, last_name, company)<BR></BR>    print u<BR></BR>    users.append(u)<BR></BR></PRE>
<P>Under certain conditions, this is more efficient
than <CODE>fetchall()</CODE>, and no worse.</P>
</DIV>


<DIV class="slide">
= Sequences =
<H1>Dictionaries as parameters</H1>
<P>Python classes typically store attributes in <CODE>__dict__</CODE>,
so you can get away with this:
</P><PRE>u = User(...)<BR></BR>c = db.cursor()<BR></BR>c.execute("""INSERT INTO users<BR></BR>(userid, first_name, last_name, company)<BR></BR>VALUES (%(userid)s, %(first_name)s,<BR></BR>%(last_name)s, %(company)s)""", u.__dict__)<BR></BR>db.commit()<BR></BR></PRE>
</DIV>


<DIV class="slide">
&gt;&gt;&gt; l = ['a','b','c','d','e']<br />&gt;&gt;&gt; print l[0]<br />a<br />&gt;&gt;&gt; print l[-1]<br />e<br />&gt;&gt;&gt; print l[2:4]<br />['c', 'd']<br />&gt;&gt;&gt; s='abcde'<br />&gt;&gt;&gt; print s[2:4]<br />cd<br />&gt;&gt;&gt; print s[::2]<br />ace<br />&gt;&gt;&gt; print s[::-1]<br />edcba<br />&gt;&gt;&gt; l.append(s)<br />&gt;&gt;&gt; print l<br />['a', 'b', 'c', 'd', 'e', 'abcde']<br />
<H1>Multi-row INSERT</H1>
<PRE># users is a list of (userid, first_name, last_name, company)<BR></BR>c = db.cursor()<BR></BR>c.executemany("""INSERT INTO users<BR></BR>(userid, first_name, last_name, company)<BR></BR>VALUES (%s, %s, %s, %s)""", users)<BR></BR>db.commit()<BR></BR></PRE>
<P>In MySQLdb, this is converted internally to a multi-row INSERT,
which is reported to be 2-3 orders of magnitude faster.
Also works for REPLACE.</P>
</DIV>


<DIV class="slide">
</div><div class="slide">
<H1>Multi-row INSERT with dictionaries</H1>
<PRE># users is a list of Users<BR></BR>c = db.cursor()<BR></BR>c.executemany("""INSERT INTO users<BR></BR>(userid, first_name, last_name, company)<BR></BR>VALUES (%(userid)s, %(first_name)s,<BR></BR>%(last_names, %(company)s)""",<BR></BR>[ u.__dict__ for u in users ])<BR></BR>db.commit()<BR></BR></PRE>
<P>This builds the parameter list with a list comprehension.</P>
</DIV>


<DIV class="slide">
= Iterators =
<H1>Never do this</H1>
<H2>Biggest MySQLdb newbie mistake of all time:
Seeing <CODE>%s</CODE> and thinking, "I should use the <CODE>%</CODE>
operator here."</H2>
<PRE>users = []<BR></BR>c = db.cursor()<BR></BR>c.execute("""SELECT userid, first_name, last_name, company<BR></BR>FROM users WHERE status='%s' and expire>'%s'""" %<BR></BR>(status, today))<BR></BR><BR></BR>for userid, first_name, last_name, company in c:<BR></BR>    u = User(userid, first_name, last_name, company)<BR></BR>    print u<BR></BR>    users.append(u)<BR></BR></PRE>
<P>Note use of <CODE>%</CODE> operator to insert parameter values.
This does <B>not</B> provide proper quoting (escaping of
<CODE>'</CODE>, <CODE>NULL</CODE>/<CODE>None</CODE>, or <CODE>\0</CODE>).
Passing them separately (as the second parameter)
ensures they are quoted correctly. However, <CODE>%</CODE>
<B>is</B> necessary if you have to insert arbitrary SQL
such as column or table names or <CODE>WHERE</CODE> clauses.</P>
</DIV>


<DIV class="slide">
* <code>iter(object)</code> returns an iterator object
<H1>To buffer, or not to buffer...</H1>
* <code>iterobj.next()</code> returns the next object
<DIV class="twocol">
* <code>StopIteration</code> is raised when there are no more objects
<H2><CODE>mysql_store_result()</CODE></H2>
&gt;&gt;&gt; # no normal person would do this<br />&gt;&gt;&gt; l = [1, 2, 3]<br />&gt;&gt;&gt; i = iter(l)<br />&gt;&gt;&gt; i.next()<br />1<br />&gt;&gt;&gt; i.next()<br />2<br />&gt;&gt;&gt; i.next()<br />3<br />&gt;&gt;&gt; i.next()<br />Traceback (most recent call last):<br /> File "", line 1, in ?<br />StopIteration<br />
<LI>Stores all rows of result set in client</LI>
<LI>Large result sets can chew up a lot of memory</LI>
<LI>Size of result set known immediately</LI>
<LI>Result set is seekable</LI>
<LI>Can issue another query immediately</LI>
<LI>Used for standard MySQLdb cursor</LI>


</DIV>
</div><div class="slide">
<DIV class="twocol">
<H2><CODE>mysql_use_result()</CODE></H2>
<LI>Sends result set row by row</LI>
<LI>Consumes resources on server</LI>
<LI>Must fetch all rows before issuing any other queries</LI>
<LI>Size of result set unknown until finished</LI>
<LI>Not seekable</LI>
<LI>Can be used with MySQLdb's <CODE>SSCursor</CODE></LI>


</DIV>
= Common iterator usage =
</DIV>


<DIV class="slide">
&gt;&gt;&gt; l = [1, 2, 3]<br />&gt;&gt;&gt; for item in l:<br />...    print item<br />...<br />1<br />2<br />3<br />&gt;&gt;&gt; d = dict(a=1, b=2, c=3)<br />&gt;&gt;&gt; for key in d:<br />...    print key, d[key]<br />...<br />a 1<br />c 3<br />b 2<br />
<H1>Optional cursor classes</H1>
<P><CODE>DictCursor</CODE> causes <CODE>fetchXXX()</CODE> methods
to return mappings instead of sequences, with column names for
keys.</P>
<PRE>users = []<BR></BR>c = db.cursor(MySQLdb.cursors.DictCursor)<BR></BR>c.execute("""SELECT userid, first_name, last_name, company<BR></BR>FROM users WHERE status=%s and expire>%s""",<BR></BR>(status, today))<BR></BR><BR></BR>for row in c:<BR></BR>   u = User(**row)<BR></BR>    print u<BR></BR>   users.append(u)<BR></BR></PRE>
<P>Note that column names happen to match <CODE>User</CODE> member names
in this case.</P>
</DIV>


<DIV class="slide">
</div><div class="slide">
<H1>Type objects and constructors</H1>
<UL>
<LI>Constructors<UL>
<LI>Date(year,month,day)</LI>
<LI>Time(hour,minute,second)</LI>
<LI>DateFromTicks(ticks)</LI>
<LI>TimeFromTicks(ticks)</LI>
<LI>TimestampFromTicks(ticks)</LI>
<LI>Binary(string)</LI>
</UL>
</LI><LI>Types<UL>
<LI>STRING</LI>
<LI>BINARY</LI>
<LI>NUMBER</LI>
<LI>DATETIME</LI>
<LI>ROWID</LI>
</UL>
</LI></UL>
<P>These are not often used with MySQLdb.</P>
</DIV>


<DIV class="slide">
= Exceptions =
<H1>Embedded server</H1>
<OL>
<LI>Build with embedded server support:
<PRE>$ export mysqlclient=mysqld<BR></BR>$ python setup.py build<BR></BR># python setup.py install<BR></BR></PRE>
</LI><LI><CODE>_mysql.server_start()</CODE></LI>
<LI>Use normally</LI>
<LI><CODE>_mysql.server_end()</CODE></LI>
</OL>
</DIV>


<DIV class="slide">
f = open("myfile", 'r')<br />try:<br />   try:<br />       for line in f:<br />           try:<br />               process(line)<br />           except TypeError:<br />                line = mangle(line)<br />                try:<br />                    process(line)<br />                except TypeError:<br />                    raise FoobarError, line<br />   except IOError, message:<br />       print "Error reading:", message<br />    except FoobarError:<br />        print "This file is totally munged."<br />   except:<br />       print "Something inexplicable happened:"<br />        raise # re-raise original exception<br />finally:<br />   f.close()<br />
<H1>Questions?</H1>
<UL>
<LI>http://sourceforge.net/projects/mysql-python</LI>
<LI>http://www.terry.uga.edu/</LI>
<LI>http://www.uga.edu/</LI>
</UL>
</DIV>


</DIV><DIV class="wikisrc"></DIV><DIV class="wikisrc"></DIV><DIV class="wikisrc">{html}</DIV></DIV></DIV><P></P><P></P>
</div><div class="slide">
 
= Odds and ends =
 
* Code blocks are delimited by indentation
** You probably do this already
** Space or tabs, your call; just be consistent
** No need for curly braces
** Less cluttered, easier to read
* End-of-line is a statement separator (so is <code><nowiki>;</nowiki></code>)
* No type enforcement
** Argument types are not checked
** Function return types are not checked
** Type checking makes your code less flexible
** If you still want it, you can add it cleanly with decorators
* Operator overloading for user-defined classes
* <i>Everything</i> is a reference (pass by reference)
* <code>None</code> object for null/missing values (equivalent to <code>NULL</code>)
 
</div><div class="slide">
 
= Odds and ends =
 
* Member access with <code>.</code> operator
** <code>instance.method()</code>
** <code>instance.attribute</code>
** <code>instance.attribute.another</code>
* Functions/methods are not the only things that are callable
* Decorators apply a callable to a function at creation time:
<br />def f(x):<br />    ...<br />
is equivalent to:
def f(x):<br />    ...<br />f = g(f)
 
</div><div class="slide">
 
= The Python DB-API =
 
* Standard API for database access
* PEP 249: http://www.python.org/peps/pep-0249.html
* By convention, module name ends with "db", i.e. MySQLdb
** Module Interface
** Connection Objects
** Cursor Objects
** DBI Helper Objects
** Type Objects and Constructors
** Implementation Hints
** Major Changes from 1.0 to 2.0
 
</div><div class="slide">
 
= Module Interface =
 
; connect(...)
: Constructor for creating a connection to the database. Returns a Connection Object.
; apilevel
: String constant stating the supported DB API level.
; threadsafety
: Integer constant stating the level of thread safety the interface supports.
 
</div><div class="slide">
 
= SQL parameter placeholders =
 
; paramstyle
: String constant stating the type of parameter marker formatting expected by the interface.
;; 'qmark'
:: Question mark style, e.g. '...WHERE name=?'
;; 'numeric'
:: Numeric, positional style, e.g. '...WHERE name=:1'
;; 'named'
:: Named style, e.g. '...WHERE name=:name'
;; 'format'
:: ANSI C printf format codes, e.g. '...WHERE name=%s'
;; 'pyformat'
:: Python extended format codes, e.g. '...WHERE name=%(name)s'
 
</div><div class="slide">
 
= Exceptions =
 
* StandardError
** Warning
** Error
*** InterfaceError
*** DatabaseError
*** DataError
*** OperationalError
*** IntegrityError
*** InternalError
*** ProgrammingError
*** NotSupportedError
 
</div><div class="slide">
 
= Connection Object =
 
; .close()
: Close the connection now
; .commit()
: Commit any pending transaction to the database. Auto-commit off by default.
; .rollback()
: Rollback any pending transaction.
; .cursor()
: Return a new Cursor Object using the connection.
; <i>exceptions</i>
: The standard exception classes; simplfies error handling in some cases
; .messages
: list of error/warning messages since last method call
 
</div><div class="slide">
 
= Cursor Object =
 
; .description
: A sequence of sequences, each of which describe a column of the result.
; .rowcount
: Number of rows affected by last query.
; .callproc(procname[,parameters])
: Call a stored database procedure with the given name.
; .close()
: Close the cursor now.
; .execute(operation[,parameters])
: Prepare and execute a database operation (query or command). Parameters: sequence or mapping.
; .executemany(operation,seq_of_parameters)
: Prepare a database operation (query or command) and then execute it against a sequence of parameters.
 
</div><div class="slide">
 
= Cursor Object =
 
; .fetchone()
: Fetch the next row of the result set as a sequence, or <i>None</i> if there are no more rows.
; .fetchmany(<i>[size=cursor.arraysize]</i>)
: Fetch a sequence of up to <i>size</i> rows; may be fewer. Zero length sequence indicates end of result set.
; .fetchall()
: Fetch all remaining rows as a sequence of rows.
; .nextset()
: Skip to the next result set. Returns a true value if there is another result set; <i>None</i> (false) if not.
; .arraysize
: Default number of rows to return with cursor.fetchmany(). Default: 1.
 
</div><div class="slide">
 
= Cursor Object =
 
; .rownumber
: Current index into result set
; .connection
: The Connection object for this cursor
; .scroll(value<i>[,mode='relative']</i>)
: Scroll to a new position in the result set (relative or absolute).
; .messages
: List containing warning/error messages since last method call (except the .fetch<i>XXX</i>() methods).
; .next()
: Fetches one row (like fetchone()) or raises <code>StopIteration</code> if no rows left. <i>Iterator protocol</i>
; .lastrowid
: Row id of the last affected row (i.e. inserting <code>AUTO_INCREMENT</code> columns)
 
</div><div class="slide">
 
= MySQL for Python =
 
* MySQL-python project on SourceForge: http://sourceforge.net/projects/mysql-python
* Current best version: 1.2.0
** Python-2.3 and newer (and maybe 2.2)
** MySQL-3.23, 4.0, and 4.1 (and maybe 5.0)
** Prepared statements not supported yet
* Older version: 1.0.1
** Python-1.5.2 (very old) and newer
** MySQL-3.22, 3.23, and 4.0 (not 4.1 or newer)
** Don't use if you can use 1.2.0
* Vaporware version: 2.0
** Python-2.4 and newer
** MySQL-4.0, 4.1, and 5.0
** Prepared statements will be supported
** Return all text columns as <code>unicode</code> by default
 
</div><div class="slide">
 
= Architecture =
 
<div class="twocol">
 
== <code>_mysql</code> ==
 
* C extension module
* transliteration of MySQL C API into Python objects
* If you use the C API, this should be very familiar
* Deprecated API calls not implemented
* Not everything (particularly fields) is exposed
* SQL column type to Python type conversions handled by a dictionary
 
</div><div class="twocol">
 
== <code>MySQLdb</code> ==
 
* Adapts <code>_mysql</code> to DB-API
* Many non-standard C API calls are exposed
* Relatively light-weight wrapper
* Implements cursors
* Defines default type mappings; easily customizable
 
</div></div><div class="slide">
 
= Opening a connection =
 
<code>connect()</code> takes the same options as <code>mysql_real_connect()</code>, and then some.
 
import MySQLdb<br /><br /><nowiki># These are all equivalent, for the most part</nowiki><br />db = MySQLdb.connect("myhost", "myuser", "mysecret", "mydb")<br />db = MySQLdb.connect(host="myhost", user="myuser",<br />                    passwd="mysecret", db="mydb")<br />auth = dict(user="myuser", passwd="mysecret")<br />db = MySQLdb.connect("myhost", db="mydb", **auth)<br />db = MySQLdb.connect(read_default_file="/etc/mysql/myapp.cnf")<br />
 
* <code>compress=1</code> enables gzip compression
* <code>use_unicode=1</code> returns text-like columns as <code>unicode</code> objects
* <code>ssl=dict(...)</code> negotiates SSL/TLS
 
</div><div class="slide">
 
= Simple query example =
 
import MySQLdb<br /><br />db = MySQLdb.connect(read_default_file="/etc/mysql/myapp.cnf")<br />c = db.cursor()<br />c.execute("""SELECT * FROM users WHERE userid=%s""", ('monty',))<br />print c.fetchone()<br />
 
== Notes ==
 
* <code>('monty',)</code> is a 1-tuple; comma required to distinquish from algebraic grouping
* Good reasons not to use <code><nowiki>*</nowiki></code>
** How many columns are being returned?
** What is the order of the columns?
* Good reasons to use <code><nowiki>*</nowiki></code>
** Table/database browser
** Lazy
 
</div><div class="slide">
 
= Multi-row query example =
 
c = db.cursor()<br />c.execute("""SELECT userid, first_name, last_name, company<br />FROM users WHERE status=%s and expire&gt;%s""",<br />(status, today))<br />users = c.fetchall()<br />
 
== Notes ==
 
* We know what the columns are
* Could use some object abstraction
 
</div><div class="slide">
 
= A simple User class =
 
class User(object):<br /><br />    """A simple User class"""<br /><br />    def __init__(self, userid,<br />                first_name=None, last_name=None,<br />                company=None):<br />        self.userid = userid<br />        self.first_name = first_name<br />        self.last_name = last_name<br />        self.company = company<br /><br />    def announce(self):<br />        """Announce User to the world."""<br />        name = "%s %s" % (self.first_name, self.last_name)<br />        if self.company:<br />            return "%s of %s" % (name, self.company)<br />        else:<br />            return name<br /><br />    def __str__(self):<br />        return self.announce()<br />
 
</div><div class="slide">
 
= Multi-row query with User object =
 
users = []<br />c = db.cursor()<br />c.execute("""SELECT userid, first_name, last_name, company<br />FROM users WHERE status=%s and expire&gt;%s""",<br />(status, today))<br /><br />for userid, first_name, last_name, company in c.fetchall():<br />    u = User(userid, first_name, last_name, company)<br />    print u<br />    users.append(u)<br />
 
might produce output like:
 
Tim O'Reilly of O'Reilly Media, Inc.<br />Monty Widenius of MySQL AB<br />Carleton Fiorina<br />Guido van Rossum of Elemental Security<br />
 
</div><div class="slide">
 
= Cursors are iterators =
 
Not necessary to use <code>c.fetchall()</code>
 
users = []<br />c = db.cursor()<br />c.execute("""SELECT userid, first_name, last_name, company<br />FROM users WHERE status=%s and expire&gt;%s""",<br />(status, today))<br /><br />for userid, first_name, last_name, company in c:<br />    u = User(userid, first_name, last_name, company)<br />    print u<br />    users.append(u)<br />
 
Under certain conditions, this is more efficient than <code>fetchall()</code>, and no worse.
 
</div><div class="slide">
 
= Dictionaries as parameters =
 
Python classes typically store attributes in <code>__dict__</code>, so you can get away with this:
 
u = User(...)<br />c = db.cursor()<br />c.execute("""INSERT INTO users<br />(userid, first_name, last_name, company)<br />VALUES (%(userid)s, %(first_name)s,<br />%(last_name)s, %(company)s)""", u.__dict__)<br />db.commit()<br />
 
</div><div class="slide">
 
= Multi-row INSERT =
 
<nowiki># users is a list of (userid, first_name, last_name, company)</nowiki><br />c = db.cursor()<br />c.executemany("""INSERT INTO users<br />(userid, first_name, last_name, company)<br />VALUES (%s, %s, %s, %s)""", users)<br />db.commit()<br />
 
In MySQLdb, this is converted internally to a multi-row INSERT, which is reported to be 2-3 orders of magnitude faster. Also works for REPLACE.
 
</div><div class="slide">
 
= Multi-row INSERT with dictionaries =
 
<nowiki># users is a list of Users</nowiki><br />c = db.cursor()<br />c.executemany("""INSERT INTO users<br />(userid, first_name, last_name, company)<br />VALUES (%(userid)s, %(first_name)s,<br />%(last_names, %(company)s)""",<br />[ u.__dict__ for u in users ])<br />db.commit()<br />
 
This builds the parameter list with a list comprehension.
 
</div><div class="slide">
 
= Never do this =
 
== Biggest MySQLdb newbie mistake of all time: Seeing <code>%s</code> and thinking, "I should use the <code>%</code> operator here." ==
 
users = []<br />c = db.cursor()<br />c.execute("""SELECT userid, first_name, last_name, company<br />FROM users WHERE status='%s' and expire&gt;'%s'""" %<br />(status, today))<br /><br />for userid, first_name, last_name, company in c:<br />    u = User(userid, first_name, last_name, company)<br />    print u<br />    users.append(u)<br />
 
Note use of <code>%</code> operator to insert parameter values. This does <b>not</b> provide proper quoting (escaping of <code>'</code>, <code>NULL</code>/<code>None</code>, or <code>

Revision as of 16:30, 13 December 2007

Check this out in [HTML format|http://esbinfo:8090/download/attachments/1704632/python-and-mysql.html] ... {html}

Python and MySQL

Andy Dustman

Office of Information Technology

Terry College of Business

University of Georgia

Andy Dustman
[1][2]
Terry College of Business
http://www.terry.uga.edu/
University of Georgia
http://www.uga.edu/

Python for the PyCurious

  • interpreted (byte-code compiler)
  • interactive (easy to test ideas)
  • object-oriented (everything's an object)
  • rapid development (5-10x C++, Java)
  • fits your brain [Bruce Eckel]
  • fits your wallet: free (OSI and GPL)
  • fun!
Introductory Material on Python
http://www.python.org/doc/Intros.html

Types


Mutable Immutable Sequence list tuple
str, unicode
Number
int, long, float
Mapping dict
Other object
The basic Python types and their mutability

Basic type examples

>>> i=1 # an int
>>> j=2**64-1 # a long integer
>>> print j
18446744073709551615
>>> f=3.14 # float (C double)
>>> c=1-1j # complex (1j is imaginary)
>>> print c
(1-1j)
>>> s="welcome to python!"
>>> s.capitalize().split() # returns a list
['Welcome', 'to', 'python!']
>>> [ word.capitalize() for word in s.split() ]
['Welcome', 'To', 'Python!']
>>> a, b = 1, 2
>>> print (a,b) # a tuple
(1, 2)
>>> a, b = b, a
>>> print (a,b)
(2, 1)

Strings

>>> "Normal string literal isn't very interesting."
"Normal string literal isn't very interesting."
>>> 'Single quotes work "same as double".'
'Single quotes work "same as double".'
>>> """Triple-quoted strings are good for long strings
... which span multiple lines."""
'Triple-quoted strings are good for long strings which span multiple lines.'
>>> r"Raw strings are useful for regexs, i.e. w+ or �"
'Raw strings are useful for regexs, i.e. \w+ or \1'
>>> u"Unicode strings work just like regular strings."
u'Unicode strings work just like regular strings.'
>>> u"72c2 7009".encode('utf-8')
'狂 瀉'
>>> print u"72c2 7009".encode('utf-8')


Strings

Lots of string methods and operators:

>>> "Split words into a list.".split()
['Split', 'words', 'into', 'a', 'list.']
>>> ' '.join(['Join', 'a', 'list', 'of', 'strings'])
'Join a list of strings'
>>> "Concatenate" + " " + "strings"
'Concatenate strings'
>>> "Multiplicity! " * 3
'Multiplicity! Multiplicity! Multiplicity! '
>>> "Parameter %s" % "substitution"
'Parameter substitution'
>>> d = dict(first_name="Vee", last_name="McMillen",
... company="O'Reilly")
>>> "Hello, %(first_name)s. How are things at %(company)s?" % d
"Hello, Vee. How are things at O'Reilly?"

Dictionaries

Python dictionaries are like perl hashes:

>>> d1={}
>>> d1['a']=1
>>> d1['b']=2
>>> d1['c']=3
>>> d1
{'a': 1, 'c': 3, 'b': 2}
>>> d2={'a': 1, 'c': 3, 'b': 2}
>>> d3=dict([('a',1),('b',2),('c',3)])
>>> d4=dict(a=1, b=2, c=3)
>>> d1 == d2 == d3 == d4
True
>>> len(d1)
3

Values can be any type, but keys must be immutable.

Sequences

>>> l = ['a','b','c','d','e']
>>> print l[0]
a
>>> print l[-1]
e
>>> print l[2:4]
['c', 'd']
>>> s='abcde'
>>> print s[2:4]
cd
>>> print s[::2]
ace
>>> print s[::-1]
edcba
>>> l.append(s)
>>> print l
['a', 'b', 'c', 'd', 'e', 'abcde']

Iterators

  • iter(object) returns an iterator object
  • iterobj.next() returns the next object
  • StopIteration is raised when there are no more objects
>>> # no normal person would do this
>>> l = [1, 2, 3]
>>> i = iter(l)
>>> i.next()
1
>>> i.next()
2
>>> i.next()
3
>>> i.next()
Traceback (most recent call last):
File "", line 1, in ?
StopIteration

Common iterator usage

>>> l = [1, 2, 3]
>>> for item in l:
... print item
...
1
2
3
>>> d = dict(a=1, b=2, c=3)
>>> for key in d:
... print key, d[key]
...
a 1
c 3
b 2

Exceptions

f = open("myfile", 'r')
try:
try:
for line in f:
try:
process(line)
except TypeError:
line = mangle(line)
try:
process(line)
except TypeError:
raise FoobarError, line
except IOError, message:
print "Error reading:", message
except FoobarError:
print "This file is totally munged."
except:
print "Something inexplicable happened:"
raise # re-raise original exception
finally:
f.close()

Odds and ends

  • Code blocks are delimited by indentation
    • You probably do this already
    • Space or tabs, your call; just be consistent
    • No need for curly braces
    • Less cluttered, easier to read
  • End-of-line is a statement separator (so is ;)
  • No type enforcement
    • Argument types are not checked
    • Function return types are not checked
    • Type checking makes your code less flexible
    • If you still want it, you can add it cleanly with decorators
  • Operator overloading for user-defined classes
  • Everything is a reference (pass by reference)
  • None object for null/missing values (equivalent to NULL)

Odds and ends

  • Member access with . operator
    • instance.method()
    • instance.attribute
    • instance.attribute.another
  • Functions/methods are not the only things that are callable
  • Decorators apply a callable to a function at creation time:

def f(x):
...
is equivalent to: def f(x):
...
f = g(f)

The Python DB-API

  • Standard API for database access
  • PEP 249: http://www.python.org/peps/pep-0249.html
  • By convention, module name ends with "db", i.e. MySQLdb
    • Module Interface
    • Connection Objects
    • Cursor Objects
    • DBI Helper Objects
    • Type Objects and Constructors
    • Implementation Hints
    • Major Changes from 1.0 to 2.0

Module Interface

connect(...)
Constructor for creating a connection to the database. Returns a Connection Object.
apilevel
String constant stating the supported DB API level.
threadsafety
Integer constant stating the level of thread safety the interface supports.

SQL parameter placeholders

paramstyle
String constant stating the type of parameter marker formatting expected by the interface.
'qmark'
Question mark style, e.g. '...WHERE name=?'
'numeric'
Numeric, positional style, e.g. '...WHERE name=:1'
'named'
Named style, e.g. '...WHERE name=:name'
'format'
ANSI C printf format codes, e.g. '...WHERE name=%s'
'pyformat'
Python extended format codes, e.g. '...WHERE name=%(name)s'

Exceptions

  • StandardError
    • Warning
    • Error
      • InterfaceError
      • DatabaseError
      • DataError
      • OperationalError
      • IntegrityError
      • InternalError
      • ProgrammingError
      • NotSupportedError

Connection Object

.close()
Close the connection now
.commit()
Commit any pending transaction to the database. Auto-commit off by default.
.rollback()
Rollback any pending transaction.
.cursor()
Return a new Cursor Object using the connection.
exceptions
The standard exception classes; simplfies error handling in some cases
.messages
list of error/warning messages since last method call

Cursor Object

.description
A sequence of sequences, each of which describe a column of the result.
.rowcount
Number of rows affected by last query.
.callproc(procname[,parameters])
Call a stored database procedure with the given name.
.close()
Close the cursor now.
.execute(operation[,parameters])
Prepare and execute a database operation (query or command). Parameters: sequence or mapping.
.executemany(operation,seq_of_parameters)
Prepare a database operation (query or command) and then execute it against a sequence of parameters.

Cursor Object

.fetchone()
Fetch the next row of the result set as a sequence, or None if there are no more rows.
.fetchmany([size=cursor.arraysize])
Fetch a sequence of up to size rows; may be fewer. Zero length sequence indicates end of result set.
.fetchall()
Fetch all remaining rows as a sequence of rows.
.nextset()
Skip to the next result set. Returns a true value if there is another result set; None (false) if not.
.arraysize
Default number of rows to return with cursor.fetchmany(). Default: 1.

Cursor Object

.rownumber
Current index into result set
.connection
The Connection object for this cursor
.scroll(value[,mode='relative'])
Scroll to a new position in the result set (relative or absolute).
.messages
List containing warning/error messages since last method call (except the .fetchXXX() methods).
.next()
Fetches one row (like fetchone()) or raises StopIteration if no rows left. Iterator protocol
.lastrowid
Row id of the last affected row (i.e. inserting AUTO_INCREMENT columns)

MySQL for Python

  • MySQL-python project on SourceForge: http://sourceforge.net/projects/mysql-python
  • Current best version: 1.2.0
    • Python-2.3 and newer (and maybe 2.2)
    • MySQL-3.23, 4.0, and 4.1 (and maybe 5.0)
    • Prepared statements not supported yet
  • Older version: 1.0.1
    • Python-1.5.2 (very old) and newer
    • MySQL-3.22, 3.23, and 4.0 (not 4.1 or newer)
    • Don't use if you can use 1.2.0
  • Vaporware version: 2.0
    • Python-2.4 and newer
    • MySQL-4.0, 4.1, and 5.0
    • Prepared statements will be supported
    • Return all text columns as unicode by default

Architecture

_mysql

  • C extension module
  • transliteration of MySQL C API into Python objects
  • If you use the C API, this should be very familiar
  • Deprecated API calls not implemented
  • Not everything (particularly fields) is exposed
  • SQL column type to Python type conversions handled by a dictionary

MySQLdb

  • Adapts _mysql to DB-API
  • Many non-standard C API calls are exposed
  • Relatively light-weight wrapper
  • Implements cursors
  • Defines default type mappings; easily customizable

Opening a connection

connect() takes the same options as mysql_real_connect(), and then some.

import MySQLdb

# These are all equivalent, for the most part
db = MySQLdb.connect("myhost", "myuser", "mysecret", "mydb")
db = MySQLdb.connect(host="myhost", user="myuser",
passwd="mysecret", db="mydb")
auth = dict(user="myuser", passwd="mysecret")
db = MySQLdb.connect("myhost", db="mydb", **auth)
db = MySQLdb.connect(read_default_file="/etc/mysql/myapp.cnf")
  • compress=1 enables gzip compression
  • use_unicode=1 returns text-like columns as unicode objects
  • ssl=dict(...) negotiates SSL/TLS

Simple query example

import MySQLdb

db = MySQLdb.connect(read_default_file="/etc/mysql/myapp.cnf")
c = db.cursor()
c.execute("""SELECT * FROM users WHERE userid=%s""", ('monty',))
print c.fetchone()

Notes

  • ('monty',) is a 1-tuple; comma required to distinquish from algebraic grouping
  • Good reasons not to use *
    • How many columns are being returned?
    • What is the order of the columns?
  • Good reasons to use *
    • Table/database browser
    • Lazy

Multi-row query example

c = db.cursor()
c.execute("""SELECT userid, first_name, last_name, company
FROM users WHERE status=%s and expire>%s""",
(status, today))
users = c.fetchall()

Notes

  • We know what the columns are
  • Could use some object abstraction

A simple User class

class User(object):

"""A simple User class"""

def __init__(self, userid,
first_name=None, last_name=None,
company=None):
self.userid = userid
self.first_name = first_name
self.last_name = last_name
self.company = company

def announce(self):
"""Announce User to the world."""
name = "%s %s" % (self.first_name, self.last_name)
if self.company:
return "%s of %s" % (name, self.company)
else:
return name

def __str__(self):
return self.announce()

Multi-row query with User object

users = []
c = db.cursor()
c.execute("""SELECT userid, first_name, last_name, company
FROM users WHERE status=%s and expire>%s""",
(status, today))

for userid, first_name, last_name, company in c.fetchall():
u = User(userid, first_name, last_name, company)
print u
users.append(u)

might produce output like:

Tim O'Reilly of O'Reilly Media, Inc.
Monty Widenius of MySQL AB
Carleton Fiorina
Guido van Rossum of Elemental Security

Cursors are iterators

Not necessary to use c.fetchall()

users = []
c = db.cursor()
c.execute("""SELECT userid, first_name, last_name, company
FROM users WHERE status=%s and expire>%s""",
(status, today))

for userid, first_name, last_name, company in c:
u = User(userid, first_name, last_name, company)
print u
users.append(u)

Under certain conditions, this is more efficient than fetchall(), and no worse.

Dictionaries as parameters

Python classes typically store attributes in __dict__, so you can get away with this:

u = User(...)
c = db.cursor()
c.execute("""INSERT INTO users
(userid, first_name, last_name, company)
VALUES (%(userid)s, %(first_name)s,
%(last_name)s, %(company)s)""", u.__dict__)
db.commit()

Multi-row INSERT

# users is a list of (userid, first_name, last_name, company)
c = db.cursor()
c.executemany("""INSERT INTO users
(userid, first_name, last_name, company)
VALUES (%s, %s, %s, %s)""", users)
db.commit()

In MySQLdb, this is converted internally to a multi-row INSERT, which is reported to be 2-3 orders of magnitude faster. Also works for REPLACE.

Multi-row INSERT with dictionaries

# users is a list of Users
c = db.cursor()
c.executemany("""INSERT INTO users
(userid, first_name, last_name, company)
VALUES (%(userid)s, %(first_name)s,
%(last_names, %(company)s)""",
[ u.__dict__ for u in users ])
db.commit()

This builds the parameter list with a list comprehension.

Never do this

Biggest MySQLdb newbie mistake of all time: Seeing %s and thinking, "I should use the % operator here."

users = []
c = db.cursor()
c.execute("""SELECT userid, first_name, last_name, company
FROM users WHERE status='%s' and expire>'%s'""" %
(status, today))

for userid, first_name, last_name, company in c:
u = User(userid, first_name, last_name, company)
print u
users.append(u)

Note use of % operator to insert parameter values. This does not provide proper quoting (escaping of ', NULL/None, or