Difference between revisions of "Python and MySQL"
Jump to navigation
Jump to search
PeterHarding (talk | contribs) (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...) |
PeterHarding (talk | contribs) |
||
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} | ||
<div class="layout"><div id="currentSlide"></div><div id="header"></div><div id="footer"> | |||
< | |||
= MySQL Users Conference 2005, Santa Clara, CA = | |||
== Python and MySQL == | |||
<div id="controls"></div></div></div><div class="presentation"><div class="slide"> | |||
= Python and MySQL = | |||
=== Andy Dustman === | |||
==== Office of Information Technology ==== | |||
==== Terry College of Business ==== | |||
==== University of Georgia ==== | |||
</div><div class="handout"> | |||
< | |||
; Andy Dustman | |||
: [mailto:adustman%40terry.uga.edu ][mailto:adustman%40terry.uga.edu ]<br /> | |||
; Terry College of Business | |||
: http://www.terry.uga.edu/ | |||
; University of Georgia | |||
: http://www.uga.edu/ | |||
</div><div class="slide"> | |||
= 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 <i>[Bruce Eckel]</i> | |||
< | * fits your wallet: free (OSI and GPL) | ||
* fun! | |||
< | <div class="handout"> | ||
; Introductory Material on Python: | |||
: http://www.python.org/doc/Intros.html | |||
</div></div><div class="slide"> | |||
= Types = | |||
< | <div id="typecomparo"> | ||
< | {| | ||
! <br /> | |||
! scope="col" | Mutable | |||
< | ! scope="col" | Immutable | ||
! scope="row" | Sequence | |||
| list | |||
| tuple<br />str, unicode | |||
|- | |||
! scope="row" | Number | |||
< | | <br /> | ||
| int, long, float | |||
|- | |||
! scope="row" | Mapping | |||
< | | dict | ||
| <br /> | |||
|- | |||
! scope="row" | Other | |||
| object | |||
| <br /> | |||
|+ The basic Python types and their mutability | |||
|} | |||
</div></div><div class="slide"> | |||
< | |||
= Basic type examples = | |||
>>> i=1 # an int<br />>>> j=2**64-1 # a long integer<br />>>> print j<br />18446744073709551615<br />>>> f=3.14 # float (C double)<br />>>> c=1-1j # complex (1j is imaginary)<br />>>> print c<br />(1-1j)<br />>>> s="welcome to python!"<br />>>> s.capitalize().split() # returns a list<br />['Welcome', 'to', 'python!']<br />>>> [ word.capitalize() for word in s.split() ]<br />['Welcome', 'To', 'Python!']<br />>>> a, b = 1, 2<br />>>> print (a,b) # a tuple<br />(1, 2)<br />>>> a, b = b, a<br />>>> print (a,b)<br />(2, 1)<br /> | |||
</ | |||
</ | |||
< | </div><div class="slide"> | ||
= Strings = | |||
>>> "Normal string literal isn't very interesting."<br />"Normal string literal isn't very interesting."<br />>>> 'Single quotes work "same as double".'<br />'Single quotes work "same as double".'<br />>>> """Triple-quoted strings are good for long strings<br />... which span multiple lines."""<br />'Triple-quoted strings are good for long strings | |||
< | which span multiple lines.'<br />>>> r"Raw strings are useful for regexs, i.e. w+ or �"<br />'Raw strings are useful for regexs, i.e. \w+ or \1'<br />>>> u"Unicode strings work just like regular strings."<br />u'Unicode strings work just like regular strings.'<br />>>> u"72c2 | ||
< | 7009".encode('utf-8')<br />'狂 | ||
瀉'<br />>>> print u"72c2 | |||
< | 7009".encode('utf-8')<br />�<br />�<br /> | ||
</ | |||
</ | |||
< | </div><div class="slide"> | ||
= Strings = | |||
Lots of string methods and operators: | |||
>>> "Split words into a list.".split()<br />['Split', 'words', 'into', 'a', 'list.']<br />>>> ' '.join(['Join', 'a', 'list', 'of', 'strings'])<br />'Join a list of strings'<br />>>> "Concatenate" + " " + "strings"<br />'Concatenate strings'<br />>>> "Multiplicity! " * 3<br />'Multiplicity! Multiplicity! Multiplicity! '<br />>>> "Parameter %s" % "substitution"<br />'Parameter substitution'<br />>>> d = dict(first_name="Vee", last_name="McMillen",<br />... company="O'Reilly")<br />>>> "Hello, %(first_name)s. How are things at %(company)s?" % d<br />"Hello, Vee. How are things at O'Reilly?"<br /> | |||
a | |||
</ | |||
</ | |||
</ | |||
</div><div class="slide"> | |||
< | |||
= Dictionaries = | |||
Python dictionaries are like perl hashes: | |||
>>> d1={}<br />>>> d1['a']=1<br />>>> d1['b']=2<br />>>> d1['c']=3<br />>>> d1<br />{'a': 1, 'c': 3, 'b': 2}<br />>>> d2={'a': 1, 'c': 3, 'b': 2}<br />>>> d3=dict([('a',1),('b',2),('c',3)])<br />>>> d4=dict(a=1, b=2, c=3)<br />>>> d1 == d2 == d3 == d4<br />True<br />>>> len(d1)<br />3<br /> | |||
</ | |||
Values can be any type, but keys must be immutable. | |||
</div><div class="slide"> | |||
< | |||
= Sequences = | |||
< | >>> l = ['a','b','c','d','e']<br />>>> print l[0]<br />a<br />>>> print l[-1]<br />e<br />>>> print l[2:4]<br />['c', 'd']<br />>>> s='abcde'<br />>>> print s[2:4]<br />cd<br />>>> print s[::2]<br />ace<br />>>> print s[::-1]<br />edcba<br />>>> l.append(s)<br />>>> print l<br />['a', 'b', 'c', 'd', 'e', 'abcde']<br /> | ||
< | |||
< | |||
</ | |||
</div><div class="slide"> | |||
< | |||
= Iterators = | |||
< | * <code>iter(object)</code> returns an iterator object | ||
< | * <code>iterobj.next()</code> returns the next object | ||
< | * <code>StopIteration</code> is raised when there are no more objects | ||
< | >>> # no normal person would do this<br />>>> l = [1, 2, 3]<br />>>> i = iter(l)<br />>>> i.next()<br />1<br />>>> i.next()<br />2<br />>>> i.next()<br />3<br />>>> i.next()<br />Traceback (most recent call last):<br /> File "", line 1, in ?<br />StopIteration<br /> | ||
< | |||
< | |||
< | |||
</ | </div><div class="slide"> | ||
< | |||
= Common iterator usage = | |||
>>> l = [1, 2, 3]<br />>>> for item in l:<br />... print item<br />...<br />1<br />2<br />3<br />>>> d = dict(a=1, b=2, c=3)<br />>>> for key in d:<br />... print key, d[key]<br />...<br />a 1<br />c 3<br />b 2<br /> | |||
</ | |||
< | </div><div class="slide"> | ||
= Exceptions = | |||
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 /> | |||
< | |||
< | |||
< | |||
< | |||
< | |||
</ | |||
</ | |||
</ | </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>%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>%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>%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>'%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}
- Andy Dustman
- [1][2]
- Terry College of Business
- http://www.terry.uga.edu/
- University of Georgia
- http://www.uga.edu/