Class 6-9: A gentle intro' to Python

Python makes programming, especially graphics programming, FUN!!

Below is a set of 'topics' to explore sequentially - together, they will give you an introductory view of Python.


Python philosophy

Quote: Python is a freely available, very high-level, interpreted language developed by Guido van Rossum. It combines a clear syntax with powerful (but optional) object-oriented semantics. Python is widely available and highly portable.

Summary: Python is *simple*, yet *powerful*. Elimination of visual clutter, adhering to consistency and useful shorthand for common tasks provides this refreshing combination. For comparison, BASIC is simple but not powerful; C++ is powerful and is in no way 'simple'!

Also, Python comes with so many modules (libraries, ie. separate, bundled code) for doing everything under the sun - 'regular expressions' to UI creation to webservers to graphics to games to data acquisition - all ready to use right out of the box, in identical ways! Hence the expression "Python comes with batteries included".


Python shells

Python is interpreted, so the best way to learn it is to interactively type commands into a Python shell. Choices for the Python shell:


Calculations

Type the following (and everything else!) into Maya's 'Python' tab, select what you typed (just like with MEL), and hit Enter at the right of the keyboard (again, just like with MEL). Maya will run the Python code inside its embedded interpreter, and provide you feedback above the Script Editor window (once again, just like with MEL).


2+5



0.2



8*4.5



a = 2

b = 3

area = a*b

area

peri= 2*(a+b)

peri

Delete names:


del(a)

del(b)

del(peri)

del(area)

area # this name is not defined anymore

Division:


7/3 # integer division!



7/-3



7.0/3



7.0/-3



7.0/3



7.0//3 # floored int div, ie integer quotient

Type 'casting':


float(7)



int (7.0)



float(7)/3



int(float(7))/3 # big noop with int() and float()



int(float(7))/3.0



int(float(7))/int(3.0) # bigger noop

mod:


# modulus (excess, or remainder):

45.67%3.2

# remove the excess, divide again, to get exact quotient

(45.67 - (45.67%3.2)) / 3.2

# int quotient, float reminder both in one step:

divmod(7.666,1.0)

Two ways to do exponentiation:


pow(2,4) # or 2**4

2**400

13*457 # !!


Modules, strings

Modules:


import math # for math calculations, we need the math "module"

math.sin(1.5)



from math import * # so we don't need to prefix 'math.'

sin(1.57)



dir()

dir(__builtins__)

dir(math)

Strings-related:




# string can be expressed in any of these eqvt. ways

a = 'flower.jpg'

a = "flower.jpg"

a = '''flower.jpg''' # uncommon, since such a 'triple quoted string' is used while documenting code

a = """flower.jpg""" # ditto comment



'Python' ' is ' 'fun' # strings placed next to each other are auto. concatenated



raw_input("Enter a number: ") # raw_input always returns a string



int(raw_input("Enter a number: ")) # int() casts a valid 'intable' string to int


Dynamic, yet strong typing

No need to declare variables' types during creation - Python infers types. Variables stay bound to their types until they're reassigned to new types.


a = 45 # implicitly, a is declared an int

type(a)



b = 12.0

type(b)



a/b # floating point /



s = a/b # s is now a float

s[0] # not a string

type(s)



s = "Python is fun" # now it is

s[0] # Each char is a string





c = 3+4j # complex # type is built in

type(c)



c.real



c.imag



d = 1+2j



c*d # complex number mult :)



c.conjugate()



c = c.conjugate()

c

# again:

c = c.conjugate()

c



c*c.conjugate() # if c=a+ib, this is (a**2+b**2)



pow((c*c.conjugate()).real,.5) # magnitude



abs(c) # simpler way!



c = complex(3,4) # constructor



complex(3,0)



complex(3) # imag. is 0



(0.5+0.8j)**0.5



((0.5+0.8j)**0.5)**2 # :)

More on numeric types: http://python.about.com/od/pythonstandardlibrary/a/numeric_types.htm


Comments


# this is a one liner comment

'''This

comments

spans several

lines'''


Recap

Keywords (from dir(__builtins__)) learned so far:


print()

type()

dir()

del()

divmod()

int()

float()

str()

help()

pow()

abs()


No pointers/refs

Every name in Python is a ref. to an object, there are no explicit pointers/refs for the user to manipulate.


Printing


print "Complex c's value is",c # expressions, separated by commas

or use:


format % values

'values' needs to be a "list". A single element of such a list does not need to be in parantheses, but multiple items DO (see below).


a=10

b=45

print "a and b are %d and %d" % (a,b) # two items to print, specified using ( )



c = 3+4j

print "complex number c is %s" % repr(c) # just one item to print, the ( ) is optional

print "complex number c is %s" % (repr(c)) # exact same result as prev. line



d = 5 + 6j

print "c and d are %r, %r" % (repr(c),repr(d)) # two items, specified as a list

%d, %f, %r, %s etc. are 'format specifiers'. repr() is a function that returns a string representation of a non-string (eg. complex) type.


Indentation

Part of the syntax!!!!!!!!!!

Each new level of indentation starts a new *block*; these can be nested.


a = 25

 print a # error!

print a # works


Data types, structures

Built-in types:


path


import sys

print "PYTHONPATH is ",sys.path # list of dirs.


help


help()



help>modules



help>keywords



help>topics



help(5)



help(1+2j)



# HTML help!

# note: 'Tcl' needs to be installed for this to work

import pydoc

pydoc.gui()



Flow control: if, while, for [and also break, continue, pass]

These constructs (if, while, for) serve as building blocks for branching and looping.

if


a,b = 10,2

# usual comparison ops: ==, !=, <, >, <=, >=

a==b

a!=b



# for two tests, use 'and', 'or' to join; 'not' negates a single test

a!=b and b==1

a!=b or b!=2



a = 12

b = 25

if a>b:

  print a

elif a==b:

  print "a and b are both equal to %d" % a

else:

  print b

# elif and else are both independently optional



c = -5

d = 5



if a<b and c<d:

  print a

else:

  print b

elif avoids deep nested blocks like the following. It takes the place of switch() in C/C++/..


if a>b:

  print "a>b"

else:

  if a==b:

    print "a==b"

  else:

    if a==100:

      print "a is 100"



# cleaner

if a>b:

  print "a>b"

elif a==b:

  print "a==b"

elif a==100:

  print "a is 100"

while


import random

random.seed(45)

running = True

while running:

  r = random.random()

  print r

  if r>0.5:

    running = False

else: # this is optional (should have been called 'after'!)

  print "end of while()"





random.seed(450)

# try above loop again..

Fibonacci sequence:


a,b=0,1 # :)

while b<10000:

  a,b=b,a+b

  print b, float(b)/a 

# qn: what is the limit of b/a?

Note that in the above, we did multiple assignments in the same statement. These can even be done to vars of different types!


a,b = 5, "Test"



a,b = b,a # !!!!



a,b,c,d = 1,2,3,4

print a,b,c,d



a,b,c,d = b,c,d,a

print a,b,c,d

for


for i in range(1,10): # iterates NINE times, not 10

  print random.random()



range(6,12) # generates a LIST: [6, 7, 8, 9, 10, 11] 



count=0

for i in range(1,100):

  r = random.random()

  if r<0.5:

    print r

    count = count+1

else: # optional

  print "printed %d values" % count

break, continue, pass

'break' breaks out of an immediate while or for loop, before loop condition becomes false - it is an 'early' exit. When a 'break' occurs, an else: block that is present will *not* get executed.

'continue' skips rest of a loop block, to return control to the top, to start a new iteration.

'pass' is simply a no-op 'filler' statement (it means "do nothing").


a = True

while a==True:

  s = raw_input('Enter something: ')

  if s=="done" or s=="exit" or s=="quit":

    print "all done, 'break'ing"

    break

  if len(s)>5:

    print "input too long - try again"

    continue

  print "You entered ",s

  a = False

else:

  print "End of while() loop"


pass



# infinite loop!

while True:

  pass # NEED this no-op stmt. to create a valid block - eqvt to while(1){} in C/C++


dir(), type()

type(), dir(), len() etc. are examples of built-in functions.

'dir' lists names in the current module or in a named module.


dir()



import math

dir(math)



dir(__builtins__) # "built in" functions, exceptions, attrs

'type()' returns an object's type.


d=56

type(d)



d='Test'

type(d)



type(5)



type({})



type([]}



type(4+6j)


The above material was a brief introduction to Python. Here are more topics that you do need to explore, to round out your knowledge: lists, tuples and dictionaries, modules, classes and functions, "Pythonisms" (eg. list comprehension), built-in modules, file handling, documentation, exceptions, math, recursion, reflection, UI, modules for CG and math..


To use Maya-related commands, use the 'maya.cmds' module (for now; soon, we'd switch to a better 'pymel.core' module)


import maya.cmds # our 'hook' into the world of Maya!!!



a = dir(maya.cmds)

for i in range(len(a)):

  print a[i]



maya.cmds.sphere() # equivalent to MEL's 'sphere()'


Here is more (the rest) of Python: functions, classes, module collections ('packages').


Functions, classes!

As we saw above, a function is defined using 'def', and is called (after defining) just like in MEL.


def sind(x):

  import math

  return math.sin(x*math.pi/180)

In the above, we're recreating MEL's useful sind() function. The 'def' keyword starts off the fn defn; next is the function's name, then comes the arg list (just like in MEL - but HUGE diff - only var names, NO TYPES!). In the body of the fn, one or more 'return' calls return values..

Once a fn has been defined, use it (call it) just like in MEL..


sind(45)

  

for i in range(90):

  print sind(i)

A huge benefit/feature: a fn can return a tuple!!!!!!

The above means that a Python fn can be written to output possibly MULTIPLE items, of possibly MULTIPLE types! This is a huge no-no in other languages, so programmers use sneaky techniques to get around this. In Python, we'll output multiples openly and with blessings :)


# returns a hodgepodge type of 7 items, as a tuple, in a single return call!!

def howCool():

  return (1,0.56,(),[5,"Test",5-3j],[],7.45,[[]])

  

# this alone is worth switching to Python :) 

a,b,c,d,e,f,g = howCool()

a

b

c

d

e

f

g

g[0] 

g[0][0] # not defined, so will generate an error





Python functions can recurse:


def fact(n):

  if n==1:

    print "End of recursion, returning 1"

    return 1

  else:

    print "Recursing: returning",n,"*","fact(",n-1,")"

    return n*fact(n-1)

    

fact(45) #!!!!!

Try fact(105)!! FYI it has 169 digits, which means they can be arranged like so (1+3+5+7+9+11+13+15+17+19+21+23+25=169):


                                        1                        

	                               081                      

	         	              39675                    

	        	             8240290                  

	       	                    900504101                

		                   30580032964              

	    	                  9720646107774            

    		                 902579144176636          

    		                57322653190990515        

  		               3326984536526808240      

 	 	              339776398934872029657    

	 	             99387290781343681609728  

		            0000000000000000000000000

For more on 105!, see http://webdocs.cs.ualberta.ca/~smillie/APE/APE43.html

Classes are used to group together related variables and functions into a useful unit. Once created, a class can be instantiated into an object, whose methods can be get/set and functions invoked. Objects can communicate with each other by calling functions on them. The class/object mechanism is the way to create custom types.

Here is a basic example, to give you a taste. Overall idea - define a class (with a name, acting as a container), place fns inside it, create "objects" (instances, ie rubber stamps, of the class), call fns as usual, but call them as 'object.function_name'.


class A:

  '''Class A, with two methods f1 and f2'''

  def f1(self): # note the 'self' keyword

    print "Running A.f1()"

  def f2(self):

    print "Running A.f2()"





a = A() # a is now a variable (object) of type (class) A



type(A)

type(a)



A

a



dir(A)

dir(a)





A.__doc__

A.__module__





a.f1() # invoke a's f1() method

a.f1(self) # error - no need to (can't) pass in 'self' - that keyword is valid only inside a class def.



a.f2()


'Packages'

The Maya site-packages directory contains paths to all sorts of packages, and contains packages too..

What are packages? They are collections of modules!


import maya # 'maya' is a package, contains maya.cmds module and others..

import os

print os.path.dirname(maya.__file__)



dir(maya) # lists all the modules in the package

dir(maya.cmds) # lists contents of the maya.cmds module


Lists, map(), "list comprehension"


l = [] # an empty list, fillable (unlike a tuple!)

l.append(1)

l.append('test')

l

l.__contains__(7)

l.__contains__('test')



l[1]

len(l)





# print each elemnt in a list

for a in l:

  print a



# roundabout:

for a in range(len(l)):

  print l[a]



l.append([]) # empty list is appended to our list

l



l[2].append('r')

l # can insert elements into a list's list element!

for i in range(10):

  l[2].append('r')

l



l[2] = {} # last list element is replaced with {}

l



del(l[1])

l



m = l + [1,2,3,9,8,7] # + concatenates lists

m

m += m



map() is a function used to iterate a function over a list or several lists. Output is a list of the function's results.


# iterate sind() for each el in range(0,360)

# replaces the for() version shown earlier!

snf = map(sind,range(0,360)) 

snf

# qn: above prints the list in a single line - how would you print

# each item in a separate line?

'List comprehension' is an alternate way to do this kind of function looping (over a range of inputs), esp. for simpler functions. Example:


li = [1,3,5,7]

li

[item*2 for item in li]

li = [item*2 for item in li] # input (li) is a list, so is the output!

li

We can safely overwrite a list variable with its processed version (which is copied to the variable AFTER getting fully formed in memory).

Q. Where is list comprehension useful?

A. provides a useful shortcut for this common pattern:


for item in list

  if condition on item

   process item, accumulate for output

The 'list comprehension' version is


[expr for item in li if condition]

Example: li = [1,2,3,4,5,6,7,8,9,10] li = [x*x for x in li if x%2 == 0] li # prints [4, 16, 36, 64, 100]

In the above, the input is a list, so is the output, which is a PROCESSED VERSION of the input list!

In summary - following are 3 eqvt ways to create this list: [0,.1,.2..0.9]


l = []

for i in range(10):

 l.append(float(i)/10)



def decimalify(x):

  return float(i)/10.0



l = map(decimalify, range(10))



l = [decimalify(i) for i in range(10)] # optionally, *can* have an if