Showing posts with label painless python. Show all posts
Showing posts with label painless python. Show all posts

Sunday, April 22, 2012

painless python for python enthusiasts

# Usage
- Email Spell Checker, spams and viruses from Emails
- python is good at data objects and memory managements
- One high level programming language
- Run programs on command-line or IDE
- By default in Mac,unix,linux
./configure; make; make install

#SEGMENT 1
- Source files with .py file extension; .pyc those are the byte code compiled version of the source code
- Intergrated Development Environments (IDEs)
- provides source code editing
- Execution framework
- Tools such as debuggers, analyzers etc
- Various IDE: IDLE, SPE, PyDev/Eclipse
- Ipython is another Interactive cmd-line has much features than the default
Interactive mode
- 3 greater than signs
- To exit: Ctrl-D in IDLE

print 'Hello World!'
print ('Hello World!') print by default add new lines
or
import sys
sys.stdout.write('Hello World!\n')

# SEGMENT 2
Python:
Object-oriented programming language
Can write scripts, applications, APIs
python is good for system capabilities
History: Invented in Holland (1989-1991)
Marketing/Executive Summary
- Robust: system support, datatypes, functionality
- Simple: simpler than VB
- Modular: import features that you need db, networking
- Flexible: java Jython extend into
- Intuitive:

"Batteries Included"
- Standard library provides core functionality

Execution Style
- Interpreted but byte-compiled ( python is interpreted but byte-compiled for performance )
like jave it also byte-compiled, if the source code is not edited it will execute the byte-compiled code directly which makes it little faster
simple arthimetic 5+4*8 = 37
2***3 = 8
s = ' python '
t = ' is cool '
s +' ' + t
python is cool
s * 2
pythonpython
s[0]= p, s[1]=y, s[-1]=n, s[3:6]='hon'
data = [43,'foo', 3.14]
data.append('bar')
data
o/p: [43, 'foo', 3.140000, 'bar']
str(data[2])
'3.14'

# Dictionary is a hash table, key followed by the value
d = {'title': 'Python Fundamental'}
d['year'] = 2009
d
o/p: {'year': 2009, 'title': 'Python Fundamentals'}
for item in data:
print item
42
foo
3.14
bar
for k in d:
print 'key:', k, 'value:', d[k]

Key: year Value: 2009
Key: title Value: Python Fundamentals
i =0
while i 5:
print i
i += 1
0
1
2
3
4
print i
5
if i == 5:
print 'i is equal to five'
i is equal to five
# Files
f = open(r'c:\windows\temp\test.txt','w')
f.write('line 1\n')
f.close()
f = open(r'c:\windows\temp\test.txt', 'r')
for line in f:
print line, # print (line, end='')
line 1
f.close()

# Functions
def foo(x):

- Use '#' to start a comment
- One statement per line
Unless semicolon(;)
- Code blocks delimited by indentation (white space)
- Use '\' to break up long lines

Block Delimitation
# foo() displays & logs its argument
def foo(x):
print "argument is ", x
f = open('log.txt','w')
if isinstance(x, str):
f.write('%s\n' %x)
else:
f.write('%s\n' % str(x))
f.close()
Statements
- simple statements fit on one line
There is no code block following e.g. print
- Compound statements have code block
First line called a header
Indented sub-block of code called a suite
Header plus ':' followed by indented suite
Indentation allows any whitespace
Use spaces not TABs E.g. TAB in Unix platform is 8 spaces whereas in Windows platform 4 characters
Recommended indentation is 4 white spaces
Use same # of spaces for each line

Syntax Elements
# Process weather check on multiple cities
checked = 0 # number of cities checked
cities = ('x','y','z','a',
'b','c')
for city in cities:
condition, wind, temp = get_weather(city)
print "Temp:", temp; checked += 1
if condition == WINDY and wind 10 \
and temp 45:
send_surfing_alert(city)

#SEGMENT 4
- Variables and Assignment
Dynamic Typing... variables not declared
- Type inferred on assignment
- var = expression

Flexible assignment syntax
- var1 = var2 = expression
- var1, var2 = expr1, expr2

Assignment operators
- primary assignment operator: =
- Augmented assignment
+=, *=, ...
- No increment/decrement
++, -- are not permitted instead we have to use += , -=

Identifier Rules
Similar to other languages
- First Character must be alphabetic
- Any others can be alphanumeric
Treat underscores (_) as alphabetic
Case-sensitive: "None" is not "none"

Style
- Variable naming guidelines
similar to other languages
- Names longer than 1 character
- Normal data in lowercase
- Class names should be Titlecases e.g. First letter should be Capital - India,Ganga
- Constants should be in CAPs
Python Gotchas
- Special names begin/end with underscores
- Do not use built-in function/data names
- Reserved words, or keywords, can't be used

Python Keywords
and,as,assert,break,class,continue,def,del,elif,else,except,exec,finally,for,from,global,finally,for,from,global,if,import,in,is,lambda,nonlocal,not,or
pass,print,raise,return,try,while,with,yield
Constants: True, False

Built-In's
def: A _____ is a special predefined(pre-build) python interpreter identifier
Built-ins are either data attributes or functions - usually the latter

Common Build-ins
Some useful built-in functions:
-informational:
dir(), help(), type() - These all will give the information about the objects
-Operational:
int(){conversion to int}, str(), range(). raw_input(), input()

#SEGMENT 5
Function Syntax
Introducing Functions
- Declared using def keyword:
def func_name ([arg1[,arg2[,...argn]...]):
func_body
def add2tome (number):
return number + 2

Functions
- Declarations:
Header line and at least one for body
Can have any number of arguments
- If no value explicitly returned...
Python returns None
None: Python's NULL or void
None: constant Boolean False

Importing & Modules
What is Importing?
Allows use of outside code
In other words to bring in the functionality, suck in some functionality into ur code.
What are Modules?
- Self-contained Python code // plug and play methodology like database import database modules things like that.
- Bring in new functionality/objects
- Import only features that you need

Import Syntax
Two different ways to access module attributes
- Use module elements with name
import os
os.getcwd() // call procedure os module followed by getcwd function
'/tmp'

- Can import individual elements into 'namespace'
from os import getcwd
getcwd() // We can call directly its just like a variable
'/tmp'

#SEGMENT 6
Keyboard Input
- How to get keyword input from user
Use python's raw_input() function
Renamed to input()
Syntax
var = raw_input(prompt)
Example
name = raw_input('Enter name: ')
Enter name: Gyani Pillala
name
'Gyani Pillala

#SEGMENT 7
400a
Introduce standard data types
- Numbers
- Sequences - Starting with index0
- Hashing - Starting with hashing key-value pair

#Standard Types

Standard Types: Numbers, Strings, Lists, Tuples, Dictionaries, Sets
Other Types: None, Funtions and Methods, Modules, Types/Classes, Iterators

Boolean Values
- Every object has an intrinsic boolean value
- Any numeric zero or empty container is False
False bool " " str
0 int [] list
0L long () tuple
0.0 float {} dict
0.0+0.0j complex None None
- All other values are True

Operators
- Apply to most data types
- Object Value Comparison
= = == !=
- Object Identity Comparison
is , is not
E.g. If x=some object, y=some object , if both happen to be pointing to the same object where "is " and "is not " can be compared against two
is , is not
- Boolean
not , and , or

Built-in Functions
- Apply to most data types
dir() object's attributes
help() show help text
type() object type
isinstance() check object type
cmp() compare 2 objects
str() string rep of object

#Numbers
- Integers (int)
No size limit
Decimal, hexadecimal,octal,binary
42, 0xAE, 0237/0o237, 0b1110
- Long intergers (long)
Merged with and replaced by integers
42L, 0xDEACADEEFABDE
- Floating-Point Numbers (float)
IEEE 754 double-precision
3.14, -6e2, 0.1
-1.1 == 1.100000000000000001
Bits do not like repeating fractions
Use round() + string formatting
Or use decimal.Decimal type
http://docs.python.org/tut/node16.html
Others Numbers
Other numeric types
complex (complex)
Boolean (bool)
Other numeric type modules
decimal
fractions
numbers
Numeric Operators
Arithmetic Operators
+ - * / // %(modulus) **(exponential)
// - Classic Division How it is different from normal division(/)
1/2 - The answer is 0 or 1.0/2.0 - The answer is 0.5 whereas
1//2 - The answer is 0.5 or 1.0//2.0 - The answer is 0.5
Unary Operator
+ -
Bit Operators ( int only )
& (bitwise and ) | (bitwise or) ^(Bitwise exclusive OR) (Left shift) (Right Shift) ~(Uniary Bitwise operator- compliment)

Numeric Built-ins
Factory
int() - When we call int we are creating integer object
float() -
complex() -
Note: Factory function works, Example when we pass a floating-point to interger function, the output is a interger itself but it won't change the floating
point number whereas it create a interger from the scratch. In other words the two format are still available
Not "casting" nor "conversion..."

Operational
abs() - absolute value
pow() - Exponential value
round() - round up the floating point value to significant figures
divmod() - It is a combination of division and the modulus operator. E.g. If you want to find out what is the Quotient and the Reminder we can get it
chr() - In python 3 all strings are unicode, you can pass the ascii value or unicode then it will turn u the apropriate string
data = []
if data:
print 'data has element in it'
else:
print 'data is empty'
o/p: data is empty
if len(data) 0:
print 'data has stuff in it'
else:
print 'data is empty'
o/p: data is empty
Note: In second case we are calling len function call. Whenever we have any funtion calls some kind of overload is associated with it.
In first case with boolen values itself we can do the purpose
dir(data) # This takes an object and tells more about it, different attributes of list
isinstance(data, list)
True
isinstance(data, int)
False
type(data)
type 'list'
1 + 2 = 3, 5 % 2 = 1, 2 ** 3 = 8, 9 ** 0.5 = 3.0, 1 / 2 = 0, 1.0/2 = 0.5, 1.0/2.0=0.5, 1.0//2.0 = 0.0
int(3.4) = 3, float(1)=1.0, round(1.185, 2) = 1.18999, '%.2f' %round(1.185, 2) = '1.19', str(round(1.185,2)) = '1.19'

#Sequences
Strings(str,unicode) # Python 2
Bytes(bytes,bytearray)
Lists(list)
Tuples(tuple)
Sequences are ...
- Array-like collections/containers
- Heterogeneous(except strings)
- Accessed by index ( starts at 0 )
Accessing the sequence elements phenomenon are called slicing
Access element(s) by "slicing"
Single-elements slice: seq[i]
Return item i for sequence seq
Where 0 = i len(seq)
Negative indices also allowed, so:
--len(seq) = i len(seq) Note: If you mention -1, it is the last element of the array

Slicing Example
S = " p y t h o n "
0 1 2 3 4 5
-6 -5 -4 -3 -2 -1
String example above
Works for all sequence types
s[2]=t, s[0]=p, s[-2]=o, s[len(s)]=Error

Multi-Element Slicing
-Access multiple element(s) with ":"
Syntax: seq[i:j]
-From i up to but not including j
Missing indices also allowed
-Means from beginning or to end
S = " python"
s[2:5] = tho
s[3:] = hon
s[:2] = py
s[-4:-1] = tho
s[:] = python

Sequence Operators
- Membership : ( in , not in )
- Concatenation : ( + )
- Repetition/Duplication ( * )

Sequence Built-ins
- Factory
str(), list(), tuple(), unicode(), bytes(), bytearray()
- Operational
len(), max(), min()

Strings
- Line an array of characters...
No char(acter) type in Python
- Immutable, as are numbers
Value cannot be changed
- Flexible string formatting
% operator or format() method
- Triple quotes allow special chars

String demo
s = 'Python'
s * 2
'PythonPython'
s
'PythonPython'
s[1]
'y'
s[-1]
'n'
s[:4]
'pyth'
s[2:6]
'thon'
s[-4:-1]
'tho'
s[:6] + ' is cool '
'Python is cool'
'-' * 20
'----------------'
'ton' is s
False
'thon' is s
True
'thon' not in s
False
hi = '''hi
there!'''
hi
'hi\nthere!'
print hi
hi
there!

String Formatting
"Name: %s, age: %d" % ('John', 35)
'Name: John, age:35'
"Name: {0}, age: {1}".format('John',35) # Quite Flexible
'Name: John, age:35'
d = { 'user': 'wes', 'page': 42 }
'http://web/%(user)s/%(page)d.html' %d
'http://web/wes/42.html'
'http://web/{user}/{page}.html'.format(**d)
'http://web/wes/42.html'

String Methods
- Built-in String Method Sampler
split() split on delimiter to list e.g. pass in the delimiter will split, password file delimiter (:)
join() join list with delimiter e.g. pass lists of substing will give new string
find() substring search
strip() remove leading/trailing WS // remove at the begin and end
lower() lowercased version of string
replace() search and replace (not RE)
ljust() create left-justified string

Demo
s = 'Python is cool.'
s
'Python is cool.'
s.find('is not')
-1
s.find('is')
7
s.rfind('o')
12
s.center(25)
' Python is cool. '
s.center(25).strip()
'Python is cool.'
s.split()
['Python', 'is', 'cool.']
'::'.join(s.split())
'Python::is::cool.'
'.'.join(reversed(s.split()))
'cool..is.Python'
s.title()
'Python Is Cool.'
s.upper()
'PYTHON IS COOL.'
s.title().swapcase()
'pYTHON iS cOOL.'
s.isalpha() # It chech the entrie string is all about alphabetic but in our case it has space and fullstop
False
s.endswith('.')
True
s.replace('.','!')
'Python is cool!'

String Miscellany
- Operators and Operational Built-in Functions
raw_input() user input
ord() ASCII/Unicode value
r'c:\tmp\new' raw string # Message to the Interpreter don't do any special conversions like \t tab or \n new line
u'n\xe9' Unicode string
b'n\xc3\xa9' bytes string

Strings and Bytes
- Unicode default string type in 3
- Introduce byte types for 8-bit chars
2. x 3.x Mutalbe
str("") bytes(b"") no
unicode(u"") str("") no
N/A bytearray yes

Lists and Tuples
- Ordered heterogeneous containers
- Lists
Mutable, resizable
Feature many helper methods
- Tuples
Immutable
Transport data to/from functions
Not truly meant for user manipulation

List Methods
- Built-in Methods
append() add object to end
insert() add object anywhere
remove(), pop() delete objects
reverse() reverse in-place
sort() sort in-place
extend() merge another list
*count() number of occurrences
*index() first index of value

Demo
m = ['core', 'programming',9,2007]
m
['core', 'programming', 9 2006 ]
m.append('prentice Hall')
m
['core', 'Programming', 9, 2007, 'prentice Hall']
m.insert(1,'pytho')
m
['core', 'Pytho', 'Programming', 9, 2006, 'Prentice Hall']
2006 in m
True
m.pop(3)
9
m
['core','python','programming',2006,'prentice hall']
m[2:4] = ['Fundamentals', 2008 ]
m
['core','python','Fundamentals', 2008, 'Prentice Hall']
m.remove('core')
m
['Python','Fundamentals',2008, 'Prentice Hall']
m.remove('c')
error

List Comprehensions
- Lists:
Highly-used data type
Most common operations:
choose elements that fit criteria
Apply function to each element
- List Comprehensions("listcomps")
construct lists using logic
Basic syntax:
[ expr for var in iterable ]

Demo
range(10)
[0,1,2,3,4,5,6,7,8,9]
[ i for i in range(10)]
[0,1,2,3,4,5,6,7,8,9]
[i for i in range(10) if i%2 ==0]
[0,2,4,6,8]
[i*3 for i in range(10) if i%2 ==0]
[0,6,12,18,24]
[i//3 for i in range(10) if i % 2 == 0]
[0,0,1,2,2]

- Generator Expressions
List Comprehensions
Not as efficient with memory
Entire list must be created
Generator Expressions
Alternative to list comprehensions
"Generate" Successive values
Nearly identical basic sytax
- (expr for var in iterable )

Hashing Types
- Dictionaries (dict)
- Mutable Sets (set)
- Immutable Sets ( frozenset )

- Hashed collection/container objects
- Accessed by iterator ( or key for dicts )
- Heterogeneous
- Unordered by definition

Dictionaries
- Python's only mapping type
Map keys to values
Mutable, resizable hash tables
- Heterogeneous
Values are arbitrary Python Objects
- Keys are scalar ( must be hashable )
Strings, numbers, etc
- No( key ) collisions allowed

Dictionary Methods
Common Built-in Methods
get() get value given key
update() merge another dict
setdefault() return value, set if nec
pop() remove & return value
items() all key-value pairs
clear() clear dictionary
fromkeys() make new dict w/keys

Dictionary Operations
General Usage
- Number of key-value pairs:
len()
- Key membership:
if k in d: # or not in
- Loop through all keys:
for k in d:
Hashing works pretty faster than the normal list

Demo
d = { 'title': ' Core Python Programming'}
d
{ 'year': 2006, 'title': 'Core Python Programming'}
'year' in d
True
'pub' in d
False
d['title']
'Core Python Programming'
d['pub']
Error, Boc the key pub is not existing
d. get ('pub', 'N/A')
'N/A'
d.setdefault('pub', 'prentice Hall')
'Prentice Hall'
d
{ 'year': 2006, 'pub': "Prentice Hall', 'title': "Core Python Programming'}
d['pub']
'Prentice Hall'
for k in d:
print k.ljust(8), d[k]

year 2006
pub Prentice Hall
title Core Python Programming

for k, v in d.items():
print k.ljust(8), v

year 2006
pub Prentice Hall
title Core Python programming
d.items()
[('year', 2006), ('pub', 'Prentice Hall'), ('title', 'Core PYthon Programming')]
d.iteritems() # Belongs to python 3
dictionary-itemiterator object at 0x11abfa0

Sets and Frozensets
- def: a set is a finite collection of objects
- Mutable(set) or immutable (frozenset)
- No key-value pairs, just values
- Unordered and heterogeneous
- Hashed, like dictionary keys
- No collisions means no duplicates
- Remember Venn diagrams ?

Sets Avoids the same numbers repetition
Demo
a = [1, 4, 6, 3]
b = (9, 5, 2, 7, 5, 3)
a, b
([1, 4, 6, 3], (9, 5, 2, 7, 5, 3))
c = set(a)
d = set(b)
c
set ([1, 3, 4, 6])
d
set ([9, 2, 3, 5, 7])
c.intersection(d)
set([3])
c & d # Intersection
set([3])
c | d # Union of C and D
set([1, 2, 3, 4, 5, 6, 7, 9])
c |= d # Assigning the value of C and D
c
set([1, 2, 3, 4, 5, 6, 7, 9])
c.add(8)
c
set ([1, 2, 3, 4, 5, 6, 7, 8, 9])
c - d # Substract the value from the set
set ([8, 1, 4, 6])

# Set Operations
- in,not in, =, =, &, |, -, ^
- for: len().max(),min()
- Augmented assignments (&=, etc )
- Set Literals {1, ...} ({} is empty dict)
- Set comprehensions { x for x in...}

= All set types have methods
- Only sets can be modified

We gave you very high level overview
Picked up some hands on work

#Summary
- Overview of standard Data types
- Operations & built-in/factory functions
- Usage examples
Next: Objects and Memory Model

#Objects and Memory Model
Objects and Memory Model

- Introducing object references
- Tracking the reference count
- Categorizing the standard types

Python manages the memory with object references... reference count
object are the primary abstraction in python


#Objects

- def: each unit of data or logic in Python is an object, the primary abstraction
All objects will be having 3 attributes mandatory

- Attributes that all objects have
- Identity
- Type
- Value
- All read-only except perhaps value
There is no way we can change the objects Identity and Type

Objects are tagged with garbage collection , when the reference count goes to 0... That basically help python manages memory

# References
- Objects created when requested/needed and (generally*) assigned as a reference
- Variables merely references to objects
- References also called aliases
- Memory management: reference count
- Count in/decrements based on usage
- Objects GC'd when "refcount" goes to 0

# More on References
x = 1
y = x
x === 1 === y
Both are looking/referencing at single object
Objects are most important thing, variables are just referencing...

- Variables...
- Don't "hold" data per se
- Just names that "point" to objects
- Can create additional aliases to objects
- Be aware of cyclic references
It's really not a problem b'cos python will be having inbuilt references manager, but keep in mind when u create a complex program object relationship

#Refcount Increment

- Object's reference count incremented when:
- It ( the object ) is created ( and assigned )
foo = 'Python is cool!'
- Additional aliases for it are created
bar = foo
- Passed to a function ( new local reference )
spam (foo) # function local variable will be pointing..
- Becomes part of a container object
lotsaFoos = [123, foo, 'xyz']

# Refcount Decrement
- Object's reference count decremented when:
- A local reference goes out-of-scope
i.e. when spam() ends
- Alias explicitly destroyed
del bar # or del foo
- Alias reassigned to different objects
bar = 42
- Object removed from container/collection
lotsaFoos.remove(foo)
- Container itself is deallocated
del lotsaFoos # or out-of-scope

#Categorizing Types
- Why categorize the standard types ?
- Make you learn them faster
- Have you understand them better
- Be able to "see" how they work internally
- Encourage more proficient programming
- Three Models
- Storage
- Update
- Access

# Storage Model
- How data is stored in an object
- Can it hold single or multiple objects ?

Model Category Standard Type(s)

literal/scalar numbers, strings
container lists,tuples, dicts, sets

English keywords
This is not like rocket science
That's all i wanted to say in the storage model
I am just trying to put myself into your shoes

Sets:
mutable sets : sets
Immutable sets : frozen sets

# Update Model
- Can an object's value be updated?
- Mutable == yes and immutable == no
- There is one of each set type
- The 'bytearray' type is mutable

Model Category Standard Type(s)

mutable lists, dicts
immutable numbers, strings, tuples

# Access Model
- How data is accessed in an object
- Directly, via index, or by key
- Primary model for type differentiation

Model Category Standard Type(s)

direct numbers, sets
sequence strings,lists,tuples
mappings dicts

# Combining All Models
Data Type Storage Model Update Model Access Model

numbers literal/scalar immutable direct
strings literal/scalar mutable sequence
lists container mutable sequence
tuples container immutable sequence
dicts container mutable mapping
sets(Two types ) container im/mutable direct

# Interning of Objects
- Exception to the general rule
- Some strings and integers are "interned"
- Integers in range (-5, 257 ) [ currently ]
- Oft-used, single-character, or empty strings
- Primarily for performance reasons

x = 4
y = 4
x == 4 == y
x = 4.3
y = 4.3
x == 4.3
y == 4.3

#What is the output here ( and WHY )?

x = ['foo', [1,2,3], 1.2 ]
y = list(x) # copy x; or x[:]

y[1][0] = 4
print x
print y

# Explanation
- Create list object x
- Make a copy and assign to y
- Use y to reference & change value

x = ['foo',[1,2,3], 1.2 ]
y = x[:]
y[1][0] = 4
print x
print y

x == ['foo', [1,2,3], 1.2 ]
y == ['foo', [1,2,3], 1.2 ]


#Answer
Unfortunately we get 4 2 3 in both cases
We just copied the references

x [ , , ] === [ 'foo', [4,2,3], 1.2 ] === y [ , , ]

Both x and y are looking at the same list, that why the data change in the list effected both the objects x & y

#Copied References
- You did copy the list, but you also
- Copied its references and changed one that is mutable (objs not copied )

" Shallow copy " -- It just copies the references it won't copy the objects

English
That is the punch line here
you swear the code is correct, but still it's not working the way you wanted it to work

# Copying Objects
- Always tricker with mutable objects
- Let's copy a list x as list y:
- Creates an alias not a copy
y = x # Conditions Checking ... - x == y and x is y ( It gives True )

- Create a shallow copy ( Copies the references ... )
y = x[:] # Condition Checking .. x == y but x is not y

- Create a deep copy ( Copies the objects ... ) [ both x and y no longer points to the same objects ]
y = copy.deepcopy(x)

## Summary
# References and "Refcounting"
# Storage, Update, & Access Models
# Interning and Copying Objects
# Eye-Opening Quizzes
# Eye-opening Quizzes
# Next: Loops and Conditionals

# Loops and Conditionals
- Syntax Considerations
- Conditional Statements
- Looping Mechanisms
- Loop "Helpers"

#Syntax Considerations
- Code delimitation by indentation
- Code blocks called "suites"
- Indentation must be consistent
- Try to use only spaces (no TABs )
- First "dedented" line not part of block
- Major players likely to be "compound"
- Helpers either BIFs or "simple" statements

# if Statement
- Conditionally execute code
- Parentheses not required

Syntax:
if conditional expression:
# If true, execute this suite
# now outside of it clause

- Example:
if i 100:
print "greater then 100"
print "always see output here "

English :
If you have experience in other programming languages, it should be fairly straight forward.

# if-else Statement
- else used with if for opposing condition

- Example:
if i 100:
print " Greater than 100"
else:
print " Less than or Equal to 100"
print "Execution Resumes Here "

# if-elif-else
- Python's "else-if" spelled elif
- Example:
# prompt, get, and check input
data = raw_input("Enter 'y' or 'n' :") # python 2
data = input("Enter 'y' or 'n' :") # python 3

if data[0] == 'y' :
print "You typed 'y'." # 'y' key
elif data[0] == 'n':
print "You typed 'n'." # 'n' Key
else:
print 'Invalid key!' # other key

Note:
python 3 , the print turning out into function...
It's mandatory that the string should be in double quotes

Demo
def demo():
data = raw_input('Enter "y" or "n": ').stip() or .lower()
if data[0].lower() == 'y': or if data[0] == 'y' or data[0] == 'n':
print 'you typed "y"'
elif data[0] == 'n':
print "you typed "n"'
elif data == 'gooj':
print "you got out of jail free.'
else:
print 'You entered invalid key.'
demo()
Enter "y" or "n": YES
You typed "Y"
demo()

# Ternary Operator
- Ternary operator aka conditional expression
- Syntax in other languages:
C ? T : F

- In python:
- 2.5+: T if C else F
- 2.5: (C and [T] or [F]) [0]
- Example:
x, y = 1, 2
max_xy = x if x y else y
max_xy

# Looping Mechanisms

- def: a_ executes a block of code...
- For a specific number of times
- Iterates over a (not nec. finite) dataset, or
- Until a condition is no longer satisfied
- Looping Statements
- Primarily used to iterate over data(for)
- Primarily used as a counting loop (while)
- Helpers
- Built-in Functions (BIFs)
- Auxiliary (simple) statements

# for Loops
- The python for loop is its most powerful
- The most commonly-used Python loop
- It traverses an "iterable," setting the loop variable to each successive value
- Syntax:
for variable in iterable:
# body of loop (suite)

# Iterables
- def: an iterable is any data structure that supports simple element traversal
- Sequence (strings, lists, tuples )
- Sequence-like objects:
- Iterators, lines of a file, keys of a dictionary, generators, sets, etc.

# range() Built-in
- range(): helper for Python for loops
- Python for more like shell foreach
- Iterating rather than traditional counting
- range() morphs counting into iterating
- range() returns list (2.x) or iterator (3.x)

- Full and Short Syntax (int arguments):
range(start, end[, step])
range([start,] end )

Demo
my_str = 'Python is cool.'
my_list = [3.14,'foo',42]

for c in my_str:
print c
print (c) # python 3
p
y
t
h
o
.... # It just prints character by character
for item in my_list:
print item
3.14
foo
42

for i in range(1, 10):
print i
1
2
3
4
... # prints upto 9

English:
This is just a very high level overview

for i in range(1, 10, 2):
print i
1
3
5
7
9
range(1, 10 )
[1,2,3,....9]

# More on range()
- Examples:
range(5) [0,1,2,3,4]
range(6, 9) [6,7, 8]
range(2, 12, 3) [2,5,8,11]

- 2.x: use xrange() to get iterator version
- Lazier, more memory-friendly
- Raplaces and renamed as range() in 3.x
- range() lets for loops iterate over index:
for i in range(len(data)):
print data[i]

# Item or Index
music = ('Poe', 'Gaudi', 'Freud')
for i in range(len(music)):
print 'album %d: %s' %(i, music[i])

album 0: Poe
album 1: Gaudi
album 2: Freud

- In the above, we iterate by index
- We can also iterate by item/element as usual:
- for album in music: print album
- But there is no access to the index above:
sometimes we want bothe the item and the index

# enumerate() Built-in
- enumerate(): another helper for Python for loops
- Returns a special-purpose iterator
- Iterator emits both index and item!!!
- Example:
music = ('Poe', 'Gaudi', 'Freud')
for i, album in enumerate(music):
print 'album %d: %s' % (i, album)

album 0: Poe
album 1: Gaudi
album 2: Freud

# while Loops
- while loops are conditionals, like a looping if
- Suite executed until condition no longer satisfied
- Suite not executed if condition was never True
- Test performed before each loop interation
- Common use cases:
- A counting loop
- An infinite loop
- Syntax:
while conditional_expression:
# body of loop

Demo
while i 5:
print i
i += 1
0
1.... # prints till 4

# Infinite while loop
not_done = True
while not_done:
# Some block of code to do something
# when finished hopefully call "break"
break
range and enumerate are built-in functions

# break statement
- break terminates a loop, regardless of conditional

- Syntax:
# inside the body of a loop
break
- Example:
while True: # infinite loop
:
if error: # break out of loop
break

# continue statement
- continue terminates current loop iteration
- It does not necessarily start the next loop
- Syntax:
# inside the body of a loop
continue

- Example:
while Ture: # infinite loop
:
if skip_reminder: # skip rest of loop
continue
# code which may be skipped

# pass statement ( Python specific )
- pass performs no action
- Python's "no operation" (NOP) statement
- Used when statement required syntactically
- Good as placeholder for code to be written
- Syntax:
# inside my suite
pass

- Example:
class NetworkError(IOError):
pass

# else Statement
- else can also be used for loops
- Unique compared to other lanugages
- Executed if loop not broken out of

- Syntax:
loop: #While or for
suite1 # normal loop block
else:
suite2 # only w/o break

# Loop else Example
- Prime numbers and the search for a divisor:

value = int(raw_input("Enter #: "))
counter = int(value ** 0.5) # sq root

while counter 1: # stop at 2
if value % counter == 0:
print(value, "is composite")
break
counter -= 1
else:
print (value, " is prime ")

# Summary
- Review indentation as delimitation
- Introduce family of if statements
- Python's Looping Types: for and while
- Define iterables
- Explore roles of loop helpers
- Next Lesson: Files and Input/Output

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 !