CS 3723
  Programming Languages  
  1. Python Introduction   

Reference links given below will be to the online book: Building skills in Python


1.1. Simple Loops and Conditionals, Output, Formatting: Fibonacci Numbers: Here are two versions of a sample Python program that prints successive Fibonacci numbers, five to a line. Below are equivalent C and Ruby programs.

Fibonacci Number Example
Python Version Using  print  "from the future"
# fib.py: standard version
import sys
f0, f1, i, last = \
0,  1,  0, 30
while i < last:
    f2 = f0 + f1
    sys.stdout.write("%i\t " % f0)
    f0, f1 = f1, f2
    i += 1
    if i%5 == 0:
        sys.stdout.write("\n")
sys.stdout.write("\n")
# fib2.py: uses print function
from __future__ import print_function
f0, f1, i, last = 0, 1, 0, 30

while i < last:
    f2 = f0 + f1
    print("%i\t " % f0, end='')
    f0, f1 = f1, f2
    i += 1
    if i%5 == 0:
        print() # prints newline
print() # prints newline
C Version Ruby Version
// fib.c: same program
#include <stdio.h>
int main() {
   int F0 = 0, F1 = 1, F2,
        i = 0, last = 30;
   while (i < last) {
      F2 = F0 + F1;
      printf("%i\t",F0);
      F0 = F1; F1 = F2;
      i++;
      if (i%5 == 0)
         printf("\n");
   }
   printf("\n");
}
# ruby.rb: Ruby version


f0, f1, i, last =
 0,  1, 0,  30
while i < last
  f2 = f0 + f1
  printf "%i\t", f0
  f0, f1 = f1, f2
  i += 1
  if i%5 == 0
    printf "\n"
  end
end
printf "\n"
Common Output
% python fib.py -tt
0       1       1       2       3
5       8       13      21      34
55      89      144     233     377
610     987     1597    2584    4181
6765    10946   17711   28657   46368
75025   121393  196418  317811  514229

Items of Interest or for study:

  • Blocks of statements are given by indentation, preceded by a ":". See Python Syntax Rules and Syntax Formalities. The portion indented is called a Suite, and the sequence of blanks and tabs on each line of indentation must be exactly the same. It is suggested that you use 4 spaces for each level of indentation, and no tabs. (I was able to set up my editor so that a tab key gave me 4 spaces and no tabs.) Notice that the if inside the while has a second level of indentation. My favorite syntax error is to leave off the ":" before the suite.

  • Python has a Multiple Assignment Statement, like Ruby, but unlike C.

  • In some cases Python lets you continue a statement onto the next line (and not worry about indenting on that next line). Any time you're inside parens or brackets you can continue. Otherwise you can always continue with a "\" at the end. (I show this above. Here there was no need, since the line isn't very long, but this method does nicely show the pairing of variable with value assigned.)

  • Single statements can always have a semi-colon at the end, but it's not required, so Python seldom uses it. This lets you put more than one statement on a line, but this should usually (or always) be avoided.

  • Python has if-elif-else, for, and while statements. See Truth, Comparison and Conditional Processing, and Loops and Iterative Processing.

  • As an amazing addition for range checking, Python allows:

      if 0 < x < 8, in place of
      if 0 < x and x < 8, which in Python is equivalent and also legal.

    This common beginner error is legal in Python and can be extended to a longer list of conditions.

  • Printing: Python 2.x has a print statement. Normally it prints a newline at the end. You can use print with a trailing comma, in which case it prints a final blank. Since it seems that print always puts either a newline or a blank at the end, print in Python 2.x is not useful for general-purpose work.

    In Python 3.x this has become a print() function with a number of different features. In particular you can write print(a, b, end='') to print nothing at the end.

    Something that works for both Python 2 and 3 is to add import sys and use the function sys.stdout.write() which expects its argument to be a single string, and writes it out (without adding a newline or a blank at the end). So I plan to mostly use this.

    Alternatively, you can use Python 2, but still import the print function "from the future", as shown in the middle program above. (A science fiction concept.)

  • For the weird stuff inside the parens after write above that looks the same as a C format, see The String Formatting Operation, %, which is about a quarter of the way down the page: Strings. As an operator in this context, % takes a format string to its left, and uses it to process one operand to its right to yield a string.


1.2. Functions, Library and User-defined: Calculating π to 16 digits:

Calculating Pi Using a Square Root Formula
Python Version C Version
# pi.py: Pi using sqrt
import sys
import math

def pi(n):
    p = 2 # final product  
    b = 0 # each denominator


    for i in range(n):
        b = math.sqrt(2 + b)
        t = 2/b # term
        p *= t
        if i%2 == 0:
            sys.stdout.write(" i: " +
             ("%2i" % i) + ", t: " +
             ("%.16f" % t) + ",  p: " +
             ("%.16f" % p) + "\n")
    return p

sys.stdout.write("pi: " +
  ("%.16f" % pi(25)) + "\n")
// fib.c: same program
#include <stdio.h>
#include <math.h>

double pi(int n) {
   double p = 2; // final product
   double b = 0; // each denominator
   double t;
   int i;
   for (i=0; i < n; i++) {
      b = sqrt(2 + b);
      t = 2/b; // term
      p *= t;
      if (i%2 == 0) {
         printf(" i: %2i, t: %.16f",
            i, t);
         printf(",  p: %.16f\n", p);
      }
   }
   return p;
}

int main() {
   printf("pi: %.16f\n", pi(25));
}
Common Output
% python pi.py -tt
 i:  0, t: 1.4142135623730949,  p: 2.8284271247461898
 i:  2, t: 1.0195911582083184,  p: 3.1214451522580524
 i:  4, t: 1.0012059964703925,  p: 3.1403311569547530
 i:  6, t: 1.0000753038310954,  p: 3.1415138011443009
 i:  8, t: 1.0000047062125721,  p: 3.1415877252771600
 i: 10, t: 1.0000002941372044,  p: 3.1415923455701180
 i: 12, t: 1.0000000183835711,  p: 3.1415926343385636
 i: 14, t: 1.0000000011489731,  p: 3.1415926523865920
 i: 16, t: 1.0000000000718108,  p: 3.1415926535145942
 i: 18, t: 1.0000000000044882,  p: 3.1415926535850947
 i: 20, t: 1.0000000000002804,  p: 3.1415926535895005
 i: 22, t: 1.0000000000000175,  p: 3.1415926535897758
 i: 24, t: 1.0000000000000011,  p: 3.1415926535897931
pi: 3.1415926535897931

The above code calculates pi using 25 terms of the following infinite product (which must be solved for pi):

Here we as mainly interested in language details rather than in the algorithm to calculate the product.

Items of Interest or for study:

  • Library Functions: The sqrt function is an example of a library function. It comes from Python's math library, which is "imported".

  • User-defined Functions: This example shows a function definition: pi. [See Functions for a long discussion of functions in Python, more than you want to know.] Here are some items of interest:

    • A function definition starts with the reserved word def.
    • Don't forget the ":" before the indented suite which is the function body.
    • You must have parens after the name of the function in the first line of the definition, and parens after a call to the function. Inside the parens are formal parameters (in the definition) and actual parameters (in the function call). If there are no parameters, you still need empty parens.
    • Python does not (and cannot) specify a return type. You can always return anything you want, or not do a return (and even mix this up: sometimes returning and sometimes not). A return uses the reserved word return. When a return is encountered, that terminates the function.
    • If your function doesn't return anything, Python will still return None.

  • Output Formatting: This example shows more of the weird output formatting that was in the previous example, using the operator "%".

    • The parens in "("%2i" % i)" above are not required. Notice that we can only continue that write statement along 3 extra lines because the breaks to a new line occur inside parens.

    • The output formatting could be done all at once, as with:

        sys.stdout.write(" i: %2i, t: %.16f, p: %.16f\n"  %  (i, t, p))

      Notice that there is an initial formatting string inside double quotes, followed by the formatting operator "%", and then by the tuple "(i, t, p)" giving the three variables to be formatted.


1.3. Scope of Variables Inside Functions: In the function defined above, notice the lack of declarations: not for the function's return type (or whether it returns nothing), not for the types of the parameters. No types declared for local variables, and indeed no types declared for any variables.

By default, any variables used inside a function have local scope (are known only inside the function) in case they are given a value inside the function. If they are not given a value inside the function, they have global scope by default. A variable given a value can be made global by declaring it "global". Parameters also have local scope. The small program at the right illustrates these issues.

Notice that there are two separate b variables in this code, one inside the function and one outside, but there is only one a variable. The statement commented out inside the function causes an error: "UnboundLocalError: local variable 'c' referenced before assignment".

Scope of Variables
Inside Functions
Output
#!/usr/bin/python -tt
import sys
a = 42
b = 37
c = 28
def foo():
    global a
    a = 13
    b = -96
    # c += 1
foo()
sys.stdout.write(str(a)+
 " " + str(b) + "\n")
% ./foo.py
13 37


1.4. Arbitrarily Large Integer Arithmetic in Python: An amazing calculation of arbitrarily many digits of π: The algorithm used is too complicated to explain in this course (but see the end of this section). We're mainly interested in the programming aspects.

Digits of Pi (forever)
# pi_inf.py: digits of pi forever
import sys
k, a, b, a1, b1 = 2, 4, 1, 12, 4
while True: # or num != 0, or non-empty str
    p, q, k = k*k, 2*k+1, k+1
    a, b, a1, b1 = a1, b1, p*a+q*a1, p*b+q*b1
    d, d1 = a/b, a1/b1
    while d == d1:
        sys.stdout.write(str(d))
        a, a1 = 10*(a%b), 10*(a1%b1)
        d, d1 = a/b, a1/b1
# pi_inf2.py: digits of pi forever
from __future__ import print_function
k, a, b, a1, b1   = \
2, 4, 1, 12,  4
while True:
    p,   q,     k    = \
    k*k, 2*k+1, k+1
    
    a,  b,  a1,       b1        = \
    a1, b1, p*a+q*a1, p*b+q*b1
    
    d,   d1     =  \
    a/b, a1/b1
    
    while d == d1:
        print(d, end='')
        
        a,        a1          = \
        10*(a%b), 10*(a1%b1)
        
        d,   d1     = \
        a/b, a1/b1
Output
% python pi_inf.py -tt
31415926535897932384626433832795028841971693993751058209749445923078164 ...
  File "pi.inf.py", line 11, in 
    a, a1 = 10*(a%b), 10*(a1%b1)
KeyboardInterrupt

Of course the program won't print digits of pi "forever". For one thing, the program requires ever increasing amounts of memory, so on a real machine it would have to run out.

This program and one below only "work" because Python supports arbitrarily large integers and operations on them. (Again, the maximum size depends on the amount of memory available, but there are other limitations, such as special tables or constants.)

The next program constrains the output and prints it in a more orderly fashion, with periodic extra blanks and newlines:

Compute 1000 Digits of Pi: pi4.py Constants: settings.py, Output
# pi4.py: pi arranged in rows
import sys
import settings
k, a, b, a1, b1 = 2, 4, 1, 12, 4
c, l = 0, 1
while l <= settings.LINES + 1:
    p, q, k = k*k, 2*k+1, k+1
    a, b, a1, b1 = a1, b1, p*a+q*a1, p*b+q*b1
    d, d1 = a/b, a1/b1
    while d == d1:
        sys.stdout.write(str(d))
        a, a1 = 10*(a%b), 10*(a1%b1)
        d, d1 = a/b, a1/b1
        if c == 0:
            sys.stdout.write("\n")
            if (l <= settings.LINES):
                sys.stdout.write(("%3i" % l)+
                   ": ")
            l += 1
        if c%10 == 0:
            sys.stdout.write(" ")
        c = (c+1)%settings.DIGITS
sys.stdout.write("\n")
# settings.py: for pi4.py
LINES  = 20 # num lines
DIGITS = 50 # digits/line

% python pi4.py 3 1: 1415926535 8979323846 2643383279 5028841971 6939937510 2: 5820974944 5923078164 0628620899 8628034825 3421170679 3: 8214808651 3282306647 0938446095 5058223172 5359408128 4: 4811174502 8410270193 8521105559 6446229489 5493038196 5: 4428810975 6659334461 2847564823 3786783165 2712019091 6: 4564856692 3460348610 4543266482 1339360726 0249141273 7: 7245870066 0631558817 4881520920 9628292540 9171536436 8: 7892590360 0113305305 4882046652 1384146951 9415116094 9: 3305727036 5759591953 0921861173 8193261179 3105118548 10: 0744623799 6274956735 1885752724 8912279381 8301194912 11: 9833673362 4406566430 8602139494 6395224737 1907021798 12: 6094370277 0539217176 2931767523 8467481846 7669405132 13: 0005681271 4526356082 7785771342 7577896091 7363717872 14: 1468440901 2249534301 4654958537 1050792279 6892589235 15: 4201995611 2129021960 8640344181 5981362977 4771309960 16: 5187072113 4999999837 2978049951 0597317328 1609631859 17: 5024459455 3469083026 4252230825 3344685035 2619311881 18: 7101000313 7838752886 5875332083 8142061717 7669147303 19: 5982534904 2875546873 1159562863 8823537875 9375195778 20: 1857780532 1712268066 1300192787 6611195909 2164201989

  Items of Interest or for study:
  • Notice the astonishing run in line 16: "...49999998...", starting at the 760th decimal place. Pi is believed to be a Normal Number, so that the sequence "999999" should occur infinitely often, but it should be extremely unlikely in the first 1000 digits. (This example is also an artifact of base 10 arithmetic, which is most likely used because we have 10 "digits": pointy things attached to the ends of our arms.)

  • Notice the constant True in the while condition above. This is of course "true". All the usual suspects are False in Python: False, 0, None, zero-length strings "", and zero-length lists [ ], along with a few others. Everything else is True. See Truth and Logic for definitions of true and false in Python.

  • The link above also describes the other boolean operators in Python: and, or, and not, rather than using characters "&|!". Equal is "==" and not equal is "!=". See operators for a full list operators and their precedence.

  • Normally, constants in Python are in all caps, along with underscores. There is no compiler-enforced constant type, so people have to know not to change a variable that is all caps.

  • The program above shows the two constants in a separate file, which is then imported. For a small program like this one, we wouldn't normally use a separate file. For another run of the program with both constants changed to 100, the output is: 10000 digits of pi (which took 30 seconds on my relatively slow PC at home).

    Notice that the name of the file must have a .py at its end, while the import gives the name without the .py. Any reference to something in this file where it is imported must be preceded by the filename without the .py, as with settings.LINES above.

  • Under some conditions it may be necessary to include the line:

      sys.stdout.flush()

    after a sys.stdout.write(-). It may be necessary to assure that the output gets properly written to wherever it's going. This applies for example if you're not writing newlines, although there were no newlines in the first example of this section and that code worked on my computer.

  • For anyone interested, the calculation is a clever use of the following formula. (See Pi Algorithm for an explanation, using Ruby instead of Python.)

    This formula is a special case of a much more general formula:

    PI as continued fraction

    Plugging in z = 1 gives the previous formula, since arctan(1) = π/4. (See Wolfram Functions Pages.)

(Revision date: 2014-06-06. Please use ISO 8601, the International Standard.)