Sunday, April 22, 2012

Painless Python for proficient programmers

 == Painless Python for Proficient Programmers

- the interactive shell displays non-None expression results
- the print statement does elementary I/O to standard output
    use comma to separate the values you want to emit
    print uses space separators and newline at the end
- In real programs, you'll use very different tools:
    the logging package for logging purposes
    add strings to a HTML response object
    GUI widgets and their methods

==  Some trivial examples

print 'Hello, world!' # classic !-)
x = 23 # name x now means 23
print x # emits 23
x = 'foo' # now  x means 'foo' instead
print x # emits foo
del x # name x reverts to undefined
print x # raises an error ("exception")

y = None # name y gets bound ot None
print y # emits None

== Similarities with Java
- Both typically compiled -- bytecode (python got a  make engine built in )
    But: Python compiles implicitly ( like a built-in make ! )
- "Everygthing" inherits from object
    But: in Python, also numbers, functions, classes, ...
    "Everything is a first-class object", no " boxing/unboxing"
- uniform object-reference semantics
    assignment, argument-passing, return
- garbage collection ( but: CPython also uses RC...)
- Key "marginalia": vast standard library ( + thind party packages & tools ) , introspection, serialization, threads

Examples:
In c when you say a = b , then lot of bits copying happens
Whereas in java and python, b refers exactly the same object as a refered to
java/python both got garbage collector


== Similarities with C++
- Multi-paradigm (not just "forced" OOP )
    OOP, procedural, generic, a little FP
- Multiple inheritance
- Operator overloading
    but: not for simple assignment ( not an operator )
- "Signature-based" polymorphism
    As if "everything was a template" ( but: nicer syntax )
- abundant choices for most all "side issues"
    - GUI, Web & other networking , DB , IPc, distributed processing...

Examples
In C++ we can overload assignment operators whereas in python we cannot overload assignment operators
python exactly have the same java symentics of assignment operators

Assignment is a object reference in python. Assignment is not an operator in python
In python everything we write is a template implicitly

== Deep connection with C

"Spirit of C" @87% ( More than java or C++ despite surface syntax...), according to ISO C standard "Rationale".
1. trust the programer
2. don't stop the programmer from doing what need to be done
3. keep the lanugage small and simple
4. provide only one way to perform an operation
5. (be fast, even if not guaranteed to be portable)
  ( this is the one point out of five that's not at 100% in python]

== The Zen of Python
import this
Beautiful is better than ugly.
Explicit is better than implicit
Simple is better than complex
Complex is better than complicated
Flat is betten than nested
Sparse is better than dense
Readability counts
...
In the face of ambiguity, refuse the temptation to guess.
There should be one, preferably only one, abvious way to do it.

== Python vs Java/C++/C: Syntax
- spare syntax, no ornamentation
    - "no wasted pixels" ( Tufle )
    - no {} around blocks, just indent
    - no {} around conditions in if and while
    - generally little punctuation

Every Pixel is important
Don't put a pixel there, unless it communicates a pixel there...
(), {} - wasting a pixel

== Python vs Java/C++/C: semantics
-types: strong but dynamic
    - names have no types ( objects have types )
    - " duck typing" ( if it walks like a duck , &c )
- No "declarations", just statements
- "everything" is a first-class object
    - classes, functions, methods, modules, packages...
- The focus is no high and very high levels
    though lower levels are decently supported too

Everything is a object
module, package these are objects

== Duck Typing
Takes some work to teach the ducks to type, but the returns in productivity are quite worthwhile:-)

==Flow Control
- if :
    - O+ elif :
    - optionally: else:
- while :
    - block can contain break, continue
    - optionally: else:
        - executes if no break terminated the loop
- for in :
    - break, continue, else: like in while
- with as :
    - in 2.5, requires: from __future__ import with_statement

== Built-in simple types ( all immutable )
- numbers: int, long, float, complex
    - 23 434343434343 ox17 2.3 4.5+6.7j
    - operators: + - * ** / // % ~ & | ^ << >>
    - built-in functions: abs min max pow round sum
- Strings: plain and Unicode
    - 'single' "double" r`aw` u"nicode" \n&\c
    - operators: + (cat), * (rep), %(format)
        - rich "format language" (akin to C's printf)
    - Built-in functions: chr ord unichr
    - are immutable sequences: len, [] (index/slice), for
        - each item is a "character" ( actually: a length-1 sting )
    - tons and tons of methods: capitalize, center, ...

== Discovering methods
dir('anystring')
........
........
........
........
........

You will be getting huge numbers of methods, attributes of that object
 

== > Getting help about a method

>>> help(''.center)
Help on builtin function center:

center(...)
 S.center(width[, fillchar]) -> string

Returns S centered in a string of length withd. Padding is done using the specified fill character ( default is a space )
(END)
>>>

==> ... and then, experiment

>>> 'ciao'.center(10, '+')
'+++ciao+++'
>>> 'ciao'.center(3, '+')
'ciao'
>>> 'ciao'.center(9, '+' )
'+++ciao++'

Note: learn by doing....

== > Built-in container types

- tuple: immutable sequence
    - () (23,) (23, 45) tuple('ciao')
- list: mutable sequence ( a "vector")
    - [] [23] [23, 45] list('ciao')
- set, frozenzet: simple hashtables
    -set () set((23,)) set('ciao')
- dict: map key -> value by hashtable
    - {} {2:3} {4:5, 6:7} dict(ci='oo')
- all containers support: len(c), looping (for x in c), membership testing ( if x in c )
    - and , generally , many methods ( sets also support operators )

==> Sequences

- stings, tuples and lists are sequences (other squence types are in the standard library )
- repetition ( c*N), catenation ( C1 + C2 )
- indexing: c[i], slicing: c[i:j] and c[i:j:k]:
    - 'ciao'[2] =='a', 'ciao'[3:1:-1] =='oa'
    - always: start included, end excluded ( per koenig...)
- lists are mutable sequences ( can assign to item or slice )
    - assigning to a slice can change the length
- dicts and sets are not sequences ( though they are iterables )
    - ordering "arbitrary" ( as befits hash tables), no slicing

== > More about Containers

- mutating vs non-mutating methods
    - most mutating methods return None
    - e.g. alist.append(...), aset.add(...), adict.update(...)
- a few mutating methods return non-None:
    -alist.pop(...), adict.pop(...), adict.setdefault(...)
- set has an immutable variant (frozenset)
- dict has a variant defaultdict ( in module collections )
- also iin collections: deque( sequence with added methods... left, clear, rotate; no slicing, no sort )
- no heap type, but rather heap functions on lists(module heapq)

==> Example: making a heap ( priority-queue type )
import heapq
class heap(object):
    def __inti__(self, content=()):  // Initializer
        self._L = list (content)
        heapq.heapify(self._L)
    def push(self, item): heapq.heappush(self._L, item)
    def pop(self): return heapq.heappop(self._L)
    def replace(self, item):
        return heapq.heapreplace(self._L, item)
    def __len__(self): return len(self._L)
    def __iter__(self): return iter(self._L)
    def __repr__(self): return 'heap(%r)' % self._L

== > Comparisons, tests, truth ( or, rather, "truthiness")
- equality, identity: == !=, is , is not
- order: < > <= >=
- membership: in, not in
- "chaining": 3 <= x < 9
- falsy: numbers == 0, "", None, empty containers, False ( aka bool(0))
- truthy: anything else, True ( aka bool(1))
- not x == not bool(x) for any x
- and, or "short-circuit" (-> return an operand)
- same for built-ins any, all (-> return a bool )

==> Exceptions
- Errors( and other "anomalies" )"raise exceptions" (instances of Exception or any subtype of Exception )
- Statement raise explicitly raises an exception
- Exceptions propagate " along the call stack", terminating functions along the way, until they're "caught"
- If uncaught, an exception terminates the program
- Statement try/except can catch exceptions (also: try/finally, and elegant with to implement "RAAI" [ in 2.5, needs from __future__ import with_statement at start of module )]

==> Some RAAI examples
from __future__ import with_statement

with open('afile.txt') as f:
    for aline in f: print aline[3],
with some_lock do: a_critical_section()

import contextlib, mysqldb
with contextlib.closing(mysqldb.connect()) as db:
    with contextlib.closing(db.cursor()) as c:
    c.execute("SELECT * FROM t WHERE t.zap< 23")
    for record in c.fetchall(): ...

==> iterators and for loops

for i in c:
    ===>
_t = iter(c)
while True:
    try: i = _t.next()
    except StopIteration: break
   
also:
( for i in c ) ("genexp")
[ for in in c ("list comprehension")

==> Some examples of genexp use (w/" accumulators")
def countall(it): return sum ( 1 for x in it )
print countall(x for x in range(99) if x%5==x%7) #15

if any(x>5 for x in xs): ...

if all(x>y>z for x, y, z in zip(xs, ys, zs)): ...
# or, better(since zip makes a list...):
from itertools import izip
if all(x>y>z for x, y, z in izip(xs, ys, zs)): ...

print max(i for i, x in in enumerate(xs) if x>0)
And... We're halfway through!

=== Rapid Development with Python Django and Google App Engine
Talk Overview
- This is not a plug for Python, Django or Google App Engine
    - Except implicitly :)
- Best practices for using Django with App Engine
    - project setup
    - The "Google App Engine Django Helper" module
    - Using App Engine's db.Model instead of Django's Model Class
    - Writing unit tests
python is a Great Language
Django is a Great Web FrameWork
Google App Engine - Is a great Application Development Platform
What i am trying to tell here is , to use some of the best practices of using Django with Appengine

Today i am going to bore you to Depth Briefly...

i hope there will be plenty of time for Q&A

=== Google App Engine Recap

- Does one thing well: run web apps
- Simple Configuration
- Request handlers
- Accelerators and shortcuts

=== App Engine Does One Thing Well

- App Engine handlers HTTP requests, nothing else
    - Think RPC: request in , processing, response out
    - Works well for the web and AJAX; also for other services
- Resources are scaled automatically
    - Requests may go to the same process, serially
    - Requests may go to different processes, in parallel or serially
- Highly scalable datastore based on Bigtable (Google internal database )
    - Not SQL: no joins

Ajax webapplication

=== Configuring an App Engine Application
- An application is a directory with everything underneath it
    symbolic links are followed
- Single file app.yami in app root directory
    Defines application metadata
    Maps URL patterns ( regular expressions) to request handlers
    Separates static files from program files
- Dev server (SDK) emulates deployment environment
They have special API's

==== Request Handlers
- URL patterns are mapped to request handlers by app.yami
    yami = is a markup language similar to python
- Handler is invoked like a CGI script
- Environment variables give request parameters
    E.g. PATH_INFO, QUERY_STRING, HTTP_REFERER
- Write response to stdout
    Status (optional), headers, blank line, body

==== Request Handler Acclerators and Shortcuts
- CGI doesn't mean slow!
- Define a main() function
    Module will remain loaded, main() called for each requests
    Can use module globals for caching
    Must use "if __name__##'__main__':main()" boilerplate
- CGI doesn't mean clumsy !
- WSGI support layered on top of CGI: util.run_wsgi_app(app)
    Web services Gateway Interface, little much resemblance of CGI(Common Gateway Interface )
Interface to the web server

==== Django Project Setup for App Engine

- Django is a bit finicky about project structure
    - code in one or more 'app' (application) subdirectories
        - cannot have the same name as the project
    - Must have a single settings.py file
            - found via DJANGO_SETTINGS_MODULE environment variable
- App Engine Vs Django
    - No SQL: must use App Engine's own database
    - Top-level directoy appended at end of sys.path
    - Django 0.96.1 preloaded, which is pretty old
    - Slight differences between dev_appserver and real deployment

=== Boilerplate Files, Standard Project Lay-out

- Boilerplate files
    - These hardly  vary between projects
    - app.yami: direct all non-static requests to main.py
    - main.py: initialize Django and send it all request
    - settings.py: change only a few settings from defaults
- Project lay-out
    - static/*: static files: serverd directly by App Engine
    - myapp/*.py: app-specific python code
        urls.py, views.py, models.py, tests.py and more
    - templates/*.html: templates ( or myapp/templates/*.html)

=== Minimal app.yaml

application: myapp         # .appspot.com
version: 1

runtime: python
api_version: 1

handlers:
- url: /static
    static_dir: static
- url: .*
    script: main.py

=== The Most Minimal main.py
import os
from google.appengine.ext.webapp import util

os.environ['DJANGO_SETTINGS_MODULE'] = 'settings'
from django.core.handlers import wsgi

def main():
    app = msgi.WSGIHandler()
    util.run_wsgi_app(app)

if __name__ == '__main__':
main()

=== BARE Minimal Settings.py
import os
DEBUG = os.environment['SERVER_SOFTWARE'].startswith('Dev')
INSTALLED_APPS( 'myapp', )
MIDDLEWARE_CLASSES = ()
ROOT_URLCONT = 'myapp.urls'
TEMPLATE_CONTEXT_PROCESSORTS = ()
TEMPLATE_DIRS = ( os.path.join(os.path.dirname(__file__), 'template'),)
TEMPLATE_LOADERS = ( 'django.template.loaders.filesystem.load.template.source',)
T2 = 'UTC'

AppEngine application always runs in UTC

=== Google App Engine Django Helper

- Separate Open source project
    By two Googlers, Matt Brown and Andy smith
    http://code.google.com/p/google-app-engine-django
- Takes care of monkey-patching Django
    Loads newer version of Django if available in app root dir
- Enables using standard Django project management tool
    For unit tests and fixture loading ( development only )
- Comes with boilerplate app.yaml, main.py, settings.py

=== Getting Started

- Download and unzip appengine_helper_for_django_rN.zip
- Rename directory to 'mysite' or whatever you like
- This  becomes your project root; contents;
    COPYING, KNOWN_ISSUES, Makefile, README
- __init__.py        (empty)
- app.yaml        (edit application: only )
- appengine_django/...    (helper code lives here )
- main.py        (generic bootstrap )
- manage.py        (Django management script )
- settings.py        ( edit settings here )
- urls.py        ( edit URL mappings here )

=== Changing the Standard Set-up

- Edit app.yaml to set your application id ( can do this later )
- Create subdirectory myapp
- Edit settings.py, adding 'myapp' to INSTALLED_APPS
- In myapp, create:
- __init__.py        (empty)
- views.py        (add your view code here )
- models.py        (add your model definitions here )
- tests.py        ( add your unit tests here )
To use Django HEAD, copy the django packages here
/usr/local/google_appengine must be your App Engine SDK

=== Your First View

- Edit urls.py:
    from django.conf.urls.defaults import *
    urlpatterns = patterns('myapp.views',(r'^$', 'index'),
        # ...more here later...
    )
- Edit myaap/views.py:
    from django.httpo import HttpResponse
    def index(request):
        return HttpResponse('Hello World')
- Run it: ./manage.py runserver
- Point your browser to: http://localhost:8080

=== Using Models

- Edit models.py:
    from google.appengine.ext import db
    from appengine_django.models import BaseModel
    class Shout(BaseModel):       # subclass of db.Model
        title = db.StringProperty(required=True)
        test = db.TextProperty()
        mtime = db.DateTimeProperty(auto_now_add=True)
        user = db.UserProperty()

- Up next:
    - write views to use models
    - Use forms derived from your models with templates
   
=== Using Forms
- I tend to put forms in views.py; do as you please
 from django.shortcuts import render_to_response
 from google.appengine.ext.db import djangoforms
 import models
 class ShoutForms(djangoforms.ModelForm):
    class Meta:
        model = models.Shout
        exclude = ['mtime'm, 'user']
    query = models.Shout.gql("ORDER BY mtime DESC")
   
    def index(request):
        return render_to_response('index.html', { 'shouts': query.run(),'form': ShoutForm()})

=== Using Templates

- Put this in templates/index.html
    {% for shout in shouts %}
   
{{ shout.user }} ; {{ shout.title }}
    {{ shout.text }}
    {{ shout.mtime | timesince }}
    { % endfor %}
        {{ form }}

=== The post() Method

- Add to urls.py:
    (r'^post/$', 'post');
- Add to views.py:
    from google.appengine.api import users
    from django.http import HttpResponseRedirect
   
    def post(request):
        form = ShoutForm(request.POST)
        if not form.is_valid():
            return render_to_response('index.html', {'form':form})
    shout = form.save(commit=False)
    shout.user = users.get_current_user()
    shout.put()
    return HttpResponseRedirect('/')

=== Writing Tests

- Python has two test frameworks:
- doctest.py
    - Tests are interactive sessions captured in doc stings
    - Really easy to get started
    - Some caveats (output must be reproducible)
   
- unitest.py
    - Tests are methods in classes derived from unittest TestCase
    - Modeled after JUnit

=== Running Tests

- Use whichever test framework you like
    ./manage.py test myapp
- Runs all tests found in myapp
    - Specifically:
        - looks for doctest and unittest tests
        - looks in models.py and tests.py

=== Example Doc Tests

- Put this in myapp/tests.py
r"""
 from django.test.client import Client
 from models import *
 c = Client()
 r = c.get('/')
 r.status_code
200
 r.content
'\n\n
\n'
 s = Shout(title='yeah', text='hello world')
 key = s.put()
 r = c.get('/')
 r.stauts_code
200
assert 'yeah' in r.content
"""

=== Test Fixtures
- ./manage.py: manages the datastore used
- ./manage.py runserver: uses a persistent datastore in /tmp
- ./manage dumpdata: writes datastore contents to stdout
    - Format can be JSON (default) or XML ( not fully supported yet )
- ./manage loaddata: loads datastore from a file
- Fixtures can also be added to TestCase classes:
   
    from django.test import TestCase
    class MyTestCase(TestCase):
        fixtures = ['test_data.json', 'more_test_data']
        def testSomething(self):
            ...

Painless Python for Proficient Programmers Part II

== Function definition and calling
- def ():
    - compiled, not executed
- call name() executes the body
- last 0+ parms may have "default values", = (expr evaluates once, at def time), then the args are optional in the call
- last O+ args may be "named", =
- may end with * (tuple of 0+ positional args) and/or ** (dict of 0+ named args )

=== Example: sum of squares
- def sumsq(a, b): return a*a+b*b
  print sumsq(23, 45)

More general, higher-abstraction:
  def sumsq(*a): return sum(x*x for x in a )

Lower-abstraction, slower but still OK:
  def sumsq(*a):
    total = 0
    for x in a: total += x*x
    return total

=== Generators
- functions with 'yield' instead of return
- each call builds and returns an iterator ( object with a next method, suitable for iterating-on e.g in a for loop )
- end of funtion raises StopIteration

def enumerate(seq):    # actually a built-in...
    n = 0
    for item in seq:
        yield n, item
        n += 1
def enumerate2(seq):    # higher-abstraction variant:
    from itertools import izip, count
    return izip(count(), seq)


=== An endless generator
def fibonacci():
    i = j = 1
    while True:
        r, i, j = i, j, i + j
        yield r
for rabbits in fibonacci():
    print rabbits,
    if rabbits > 100: break

1 1 2 3 5 8 13 21 34 55 89 144

=== Closures
- def is an executable statement ( each execution creates  a new function object), and scoping is lexical, so...

def makeAdder(addend):
    def adder(augend):
        return augend + addend
    return adder
a23 = makeAdder(23)
a42 = makeAdder(42)
print a23(100), a42(100), a23(a42(100))
123 142 165

=== Decorators
@
def etc, etc

- is like:
    def etc, etc
    = ()
- Handy syntax to apply a Higher-Order-Function(HOF);
    may be a name or a call (HOF2 ...!)


=== Classes ("new-style")

class ():
 - is usually a series of def and assignment statements; names defined or assigned become attributes of new class object (functions become "methods")
- attributes of any base are also attributes of the new class, unless "overridden" (assigned or defined in the body)

=== Instantiating a class
To create an instance, just call the class:
class eg(object):
    cla = []            # class attributes
    def __init__(self):     # initializer
        self.ins = {}   # instance attributes
    def meth1(self, x ):    # a method
        self.cla.append(x)
    def meth2(self, y, z):  # another method
        self.ins[y] = z
es1 = eg()
es2 = eg()

=== Classes and instances
 print es1.cla, es2.cla, es1.ins, es2.ins
[] [] {} {}

es1.meth1(1); es1.meth2(2,3)
es2.meth1(4); es2.meth2(5,6)
print es1.cla, es2.cla, es1.ins, es2.ins
[1,4] [1,4] { 2: 3} {5: 6}

print es1.cla is es2.cla
True
print es1.ins is es2.ins
False
=== Look-up Mechanics
inst.method(arg1, arg2)
    == type(inst).method(inst, arg1, arg2)
    inst.name [[whether it later gets called, or not ...!]]
    === ("descriptors" can change this behavior...)
1. first try inst.__dict__['name']
2. else try type(inst).__dict__['name']
3. else try each of type(inst).__bases__
4. finally try type(inst).__getattr__(inst, 'name')
5. if all else fails, raise AttributeError

=== Subclassing

class sub(eg):
    def meth2(self, x, y=1): # override
        eg.meth2(self,x,y)    # super-call
        # or: super(sub, self).meth2(x,y)
class repeater(list):
    def append(self,x):
        for i in 1, 2:
            list.append(self, x)
class data_overrider(sub):
    cla = repeater()

=== Properties

class blah(object):
    def getter(self):
        return ...
    def setter(self, value): ...
        name = property(getter, setter)
    inst = blah()
Now ...

    print inst.name # like inst.getter()
    inst.name = 23  # like inst.setter(23)

=== Why properties matter:

- never "hide" attributes behind getters/setters "for flexibility"
- instead, expose interesting attributes directly
- if/when in a future release you need a getter and/or a setter ...
- write the new needed methods
- wrap them up into a property
- and all client-code of the class need NOT change!
- down with boilerpalte! NEVER code like:
    def getFoo(self): return self._foo
    def setFoo(self, foo): self._foo = foo
just name the attribute foo ( not_foo ) so it's directly available !

=== Operator Overloading

"special methods" have names starting and ending with __ ( double-underscore AKA "dunder"):
__new__, __init__, __del__  # ctor, init, finalize
__repr__, __str__, __int__  # convert
__lt__, __gt__, __eq__ ...  # compare
__add__, __sub__, __nul__ ... # arithmetic
__call__, __hash__, __nonzero__ ...
__getattr__, __setattr__, __delattr__
__len__, __iter__, __contains__
__get__, __set__, __enter__ , __exit__ ...

Python calls a type's special methods appropriately when you perform operations on instances of the type

=== An endless iterator

class Fibonacci(object):
    def __init__(self): self.i = self.j = 1
    def __iter__(self): return self
    def next(self):
        r, self.i = self.i, self.j
        self.j += r
        return r
    for rabbits in Fibonacci():
        print rabbits,
        if rabbits > 100: break

1 1 2 3 5 8 13 21 34 55 89 144

=== Built-in Functions

- Don't call special methods directly; let built-in functions and operators do it right !
    e.g: abs(x), neverx.__abs__()
- many interesting built-in functions:

  abs any all chr cmp compile dir enumerate eval getattr hasattr hex id intern isinstance iter len max min oct open ord pow range repr reversed round setattr sorted sum unichr xrange zip

- many, MANY other important types and functions in standard library modules (array, bisect, cmath, collections, contextlib, copy, functools, heapq, inspect, intertools, operator, pprint, Queue, random, re, StringIO, struct weakref, ...)

=== Example: indexing a textfile

# Build map word -> [ list of line #s containing it ]
indx = {}
with open(filename) as f:
    for n, line in enumerate(f):
        for word in line.split():
            indx.setdefualt(word, []).append(n)
# emit it in alphabetical order
for word in sorted(indx):
    print "%s:" % word,
    for n in indx[word]: print n,
    print

=== Or, a bit simpler, with a little stdlib help...

import collections
indx - collections.defaultdict(list)
with open(filename) as f:
    for n, line in enumerate(f):
        for word in line.split():
            indx[word].append(n)
    for word in sorted(indx):
        print "%s:" %word,
        for n in indx[word]: print n,
        print

=== Other possibilities given the indx map
# the 7 most popular words in the textfile
import heapq
for w in heapq.nlargest(7, indx, key=indx.get):
    print w
# lines that contain N given words
def enwhack(*words):
    words = list(words)
    r = set(indx[words.pop()])
    for w in words: r &= set(indx[w]) # intersection
    return sorted(r)

=== Importing modules
- import modulename
- from some.given.package import modulename
    - then, in either case, use modulename.blah
    - some abbreviations ( not necessarily recommended...):
        - alias module name with an 'as' clause:
        - import thisnameisfartoolong as z
            - then use z.blah
    - from thisnameisfartoolong import blah
    - from thisnameisfartoolong import * # not recommended

=== Import example
import math
print math.atan2(1, 3)
# emits 0.321750554397
print atan2(1, 3)
# raises a NameError exception
from math import atan2
# injects atan2 into current namespace
# handy for interactive use, often confusing in "real" programs
# and, always avoid (except for interactive use)...
from math import *

=== Coding your own modules
- any python source wot.py is a module
- just import wot
    - file must be in a directory (or zip file) listed in sys.path
    - also importable: the bytecode file(wot.pyc) which the python compiler created the first time you import the source
- also importable: binary native-code extension files(wot.pyd) coded in c  and using python's C API (or other compiled-to-native-code languages and tools: pyrex, Cython, SWING,...)
- Note: Google App Engine only Supports .py modules !
Every python program is a module

=== What's a module?
- a module is a simple object with attributes  -- the attributes are the modules's "top-level" names
- bound by assignment, or assigning-statements: class, def, import, from
- a modules's attributes are also known as "global variables" of the module ( there are no "global" globals!)
- a module's attribues can also be bound/unbound/rebound "from the outside" (also known as "monkey patching")
    - not good practise, but sometimes useful for testing with Mock and similar design patterns
    - do however consider the Dependency injection DP alternative

=== Modules are singletons
- the most natural and Pythonic form (automatic singletons)
- the first import loads the module ( compiling if needed ) and executes it, every other import just accesses it, specifically by using sys.modules[modulename]
- if you really need a class ( for overloading, getattr, &c)...
    import sys
    class _hidden(object): ...
    sys.modules[__name__] = _hidden()

=== Packages
- a package is a module containing other modules ( and maybe sub-packages &c )
- lives in a directory containing a file named __init__.py:
    __init__.py is the "module body"
    often empty (just "tagging" the directory as a package)
    a package's module are the dir's .py files
    subpackages are subdirs which need to contain __init__.py
- the parent directory must be on sys.path
- import foo.bar or from  foo import bar

=== " Batteries included "
    - The standard python library has > 200 modules ( plus many, many more for unit-tests, encoding/decoding, demos)
    - some are pure Python modules, some are C-coded ones
    - The App engine supports Any pure-Python modules
    - and MOST standard library C-coded modules
        -( with very few specific execptions, such as, sockets, threads)
    - plus, some specific API's ( Datastore, users, urlfetch, mail)
    - also, any pure-python modules you include with your app

=== A peculiar consequence
    - time needed for an expert programmer to learn well...
    - python itself ( the python language): 1-3 days
    - built-ins, special methods, metaprogramming, &c: 2-4 days
    - standard library (absolutely-crucial modules): 10-15 days
    - all the standard library: 30-50 days
    - all third-party offerings: ..

=== To find more nifty modules...
- http://cheeseshop.python.org/pypi: 4000+ packages, and counting!
- It's always fun to roll your own, but...:-)
- time to learn all about 4000+ packages: well, hmmmmm

=== Online resources ( to get started )
- www.python.org
- www.diveintopython.org
- (usenet) comp.lang.python
- (Mailing list) help @ python.org
- Any good search engine !

No comments: