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.
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 is interpreted, so the best way to learn it is to interactively type commands into a Python shell. Choices for the Python shell:
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:
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
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
# this is a one liner comment '''This comments spans several lines'''
Keywords (from dir(__builtins__)) learned so far:
print() type() dir() del() divmod() int() float() str() help() pow() abs()
Every name in Python is a ref. to an object, there are no explicit pointers/refs for the user to manipulate.
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.
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
Built-in types:
import sys print "PYTHONPATH is ",sys.path # list of dirs.
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()
These constructs (if, while, for) serve as building blocks for branching and looping.
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"
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 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
Exercise: let's translate the following to Python:
float $goldenAngle = 137.5*3.14159278/180.0; for($i=1;$i<=$n;$i++) { float $r = $c*sqrt($i); float $a = $goldenAngle*$i; float $x=$r*cos($a), $y=$r*sin($a); cylinder -ax 0 0 1 -hr 2 -d 3 -r $seedRad; // -s 8 -nsp 1 move $x $y 0; }// next $i
'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++
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').
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()
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
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