Skip navigation.
Home

BASIC to Python

This is a quick primer, mainly for ex-BBC BASIC programmers moving to Python, although others may find it useful. Python is a clean, simple and elegant language which provides fairly direct equivalents to most BASIC statements, but for more advanced users also goes far beyond that.

Python makes an excellent language for writing ROX applications, and most of the ROX applications are written in it. However, for this primer we only consider general programming constructs without any GUI stuff. See the other tutorials for that. You'll also want to read the official python tutorial after this.

Getting started

Here's the familiar hello world program in BASIC and in Python:

PRINT "Hello World"

becomes

print "Hello World"

  • Python keywords are lowercase.

a = 4.5
b% = 3
c$ = "Hello World"

becomes

a = 4.5
b = 3
c = "Hello World"

  • Any python variable can hold a value of any type. There are no special characters in the name indicating type.

d = None

  • There is a special value, None, different from zero or false or the empty string. This is useful in many situations. Consider, for example, a function which gets a number from the user and returns it. How should it indicate that the user has finished the list of numbers? Traditionally, a BASIC programmer might return zero or -1 to indicate this, but using None will prevent confusion if the user really does enter the reserved number.

number = input("Enter a number:")

  • INPUT is a function, not a statement, in python.

Note: use raw_input() to get the exact string the user entered. The input() function tries to evaluate whatever you enter as an expression (eg, entering "1+1" will return 2).

Interactive mode

Like BASIC, python can be run interactively (type 'python' at the command prompt). Unlike BASIC, you don't need to write PRINT to evaluate things; in interactive mode, the result of every expression is printed automatically. >>> indicates the python prompt.

>>> a = 4
>>> a
4
 
>>> a + 1
5

WHILE...ENDWHILE

You can also enter multi-line statements, such as loops, at the prompt. The '...' prompt indicates that it's waiting for more input. Enter a blank line to complete the statement.

>>> x = 1
>>> while x < 10:
...     print x
...     x = x + 1
...
1
2
3
[etc]

  • BASIC programmers normally indent their code to show loops clearly. However, BASIC itself ignores the indentation and uses the ENDWHILE marker to detect the end of the loop. Python uses the indentation directly, so no end markers are needed.

Python has a 'break' statement that can be used to exit from a loop at any point:

10 Finished% = FALSE
20 WHILE NOT Finished%
30   REM Stuff...
40   IF FNerror THEN Finished% = TRUE:ENDWHILE:GOTO 70
50   REM More stuff...
60 ENDWHILE
70 PRINT "Done"

becomes

finished = False
while not finished:
        # Stuff...
        if error(): break
        # More stuff...
print "Done"

  • Python uses # instead of REM for comments
  • A function with no arguments has empty brackets ()
  • Python doesn't have GOTO; more flexible programming structures make this unnecessary

REPEAT...UNTIL

Python doesn't have REPEAT loops. These can normally be done directly as while loops, but if not, use break for the end
condition. 'while True' is an infinite loop, as in BASIC.

REPEAT
  REM Stuff
UNTIL FNfinished

becomes

while True:
        # Stuff
        if finished(): break

IF...THEN...ELSE...ENDIF

If statements are very similar. The use of indentation means that there is no difference between single line and multiline statements. Note the `:'; this starts all nested blocks. The 'else' must go at the start of a new line (this prevents any confusion as to which IF the ELSE goes with, as is possible in BASIC).

if x > 0 and x < 10: print "OK"
else: print "Out of range!"

Like BASIC, python considers 0 to be false, and any other integer to be true. Unlike BASIC, python also allows non-integers:

>>> if "hello": print "yes"
...
yes
 
>>> if None: print "yes"
...
  • An empty string is false, a non-empty one true.
  • None is also considered to be false.

DEF FN

>>> def double(x): return 2 * x
...
>>> double(4)
8

  • There is no need for the FN prefix.

DEF PROC

>>> def hello():
...     print "Hello World"
...
>>> hello()
Hello World

  • There is no need for the PROC prefix.
  • There is no difference in python between a function and a procedure. A procedure is just a function that doesn't return anything.

>>> a = hello()
Hello World
>>> print a
None

  • To be more precise, a proceduce is a function that returns the special value `None'.
  • You need the 'print' statement to print None; the interpreter skips it to save space.

DIM ()

Python uses lists rather than arrays. They are rather more flexible (they can change size, for example).

>>> a = ["red", "green", "blue"]
>>> a
["red", "green", "blue"]
 
>>> a[0]
"red"
>>> a[1]
"green"
>>> a[2]
"blue"
>>> a[1] = "yellow"
>>> a[1]
"yellow"
  • You can use them much like BASIC arrays.

>>> a[1] = 4
>>> a
["red", 4, "blue"]

  • Just as you can store any type in any variable, you can mix types in lists.

>>> a[-1]
"blue"
>>> a[-2]
4

  • Negative indices count backwards from the end. This is very useful!

>>> a[1:3]
[4, "blue"]
>>> a[1:]
[4, "blue"]
>>> a[:1]
["red"]
>>> a[:-1]
["red", 4]

  • You can specify a start (inclusive) and end (exclusive) index to create a 'slice' of the original list. If you leave off one limit, it goes from the beginning or end.
  • [:-1] means 'all but the last element', while [1:] means 'all but the first'

LEFT$, RIGHT$, MID$

Python has none of these. Instead, you can treat a string as a list of characters and use the indexing and slicing notation as for lists:

>>> b = "Hello World"
>>> b[0]
'H'
>>> b[1]
'e'
>>> b[-1]
'd'
>>> b[-2]
'l'
>>> b[1:]
'ello World'
>>> b[:-1]
'Hello Worl'
 
>>> b[2:4]
'll'
  • Python is neat!

FOR...NEXT

Python's for loops are completly different to BASIC's. Instead of providing start, end and step values, you provide a list to loop over.

>>> for x in ["red", "green", "blue"]:
...     print "The next colour is", x
...
The next colour is red
The next colour is green
The next colour is blue

You can get the BASIC behaviour by using the range() function to create a list of numbers to loop over, but you'll find you almost never need to use this.

a$ = "Hello,Bye,Fred"
count% = 0
FOR c% = 1 TO LEN(a$)
  IF MID$(a$, c%, 1) = "," THEN count% += 1
NEXT c%
PRINT "String contains "; count%; " commas"

becomes

a = "Hello,Bye,Fred"
count = 0
for c in a:
  if c == ",": count += 1
print "String contains", count, "commas"

  • No need to loop over indices and then extract the element at that index. Just loop over the list instead.
  • Use '==' for comparisons instead of '='. Python won't let you use '=' in an IF statement, so no risk of C-style errors.
  • += notation is the same

In fact, python provides a better way to count, as we shall see later.

ON ERROR LOCAL...RESTORE ERROR

Python error handling is much simpler, and doesn't require you to create a new function for each error trapping point.

 10 INPUT "Enter a number:", number%
 20 PROCrecip(number%)
 30 PRINT "Done!"
 40 END
 50 
 60 DEF PROCrecip(number%)
 70  ON ERROR LOCAL RESTORE ERROR: PRINT "Can't do that, sorry!": ENDPROC
 80  answer = 1 / number%
 90  PRINT "Result is", answer
100 ENDPROC

becomes:

number = input("Enter a number:")
try:
        answer = 1 / number
        print "Result is", answer
except:
        print "Can't do that, sorry!"
print "Done"
  • Any error in a 'try' block jumps to the 'except' clause.

try:
        answer = 1 / number
except DivisionByZero:
        print "Can't devide by zero!"
except:
        print "Something wierd happened!"
  • You can provide multiple except clauses for different types of
    error.

If you don't provide a default handler ('except:') then the error is passed up to the enclosing 'try' block (possibly in a calling function) until it is either handled or there are no more try blocks. In that case, python prints out the error, giving not only the line number of the problem, but showing the call in each function leading to it.

In fact, you should almost never use except on its own since unexpected errors should be reported with all their details. Only try to catch errors you want to handle specially (such as division by zero above).

Objects

Time to come clean... python is an object oriented language. This is a good thing, but requires a little adjustment to appreciate fully. Now, consider variables in BASIC. We think of a variable as a box with a label (the variable's name) and a value inside it. In an OO (Object Oriented) language, the values exist outside of any boxes on their own. Variables merely 'point to' values. The difference is, two variables can point to the same value. Consider:

>>> a = [1,2,3]
 
>>> b = [1,2,3]
>>> c = b

The final result can be visualised like this:

a ----> [1,2,3]
b ----> [1,2,3] <---- c

That is, we have created two lists. 'a' points to the first, while 'b' and 'c' point to the second. The '==' test in python checks if two arrays contain equal values, while the 'is' test checks if 'two' values are actually one:

>>> a == b
True
>>> b == c
True
>>> a is b
False
>>> b is c
True

The difference is important when you change something:

>>> b[1] = 42
>>> a
[1,2,3]
 
>>> b
[1,42,3]
>>> c
[1,42,3]

On the other hand, if you assign something to 'b' (rather than modifying the thing 'b' points to), you make 'b' point at that instead:

>>> b = [4,5,6]
a ----> [1,2,3]
c ----> [1,42,3]
b ----> [4,5,6]

You might be a bit worried about this:

>>> a = 1
>>> b = a
>>> a += 1
>>> b
[ what goes here? ]

Don't worry. After assigning to 'b', 'a' and 'b' do indeed point at the same object (the number 1):

a ----> 1 <---- b

However, incrementing 'a' has the effect of creating a new number, and making 'a' point at that:

a ----> 2
b ----> 1

You cannot change the number which 'a' points to in-place (numbers are thus said to be 'immutable'). Strings are also immutable. This means you can pass lists (arrays) to functions very quickly (because you're just making another pointer to the same array). If you want to copy a list, use the slicing notation to copy the whole thing:

>>> a = [1,2,3]
>>> b = a[:]
a ----> [1,2,3]
b ----> [1,2,3]

MAKE SURE YOU UNDERSTOOD THE ABOVE!

Garbage collection

OK, so we can create lots of new objects. How do we get rid of them again? The answer is simple: when it's no longer possible to refer to an object, python will free it for you:

>>> a = [1, 2, 3]
>>> a = None
  • The first line creates three number objects and a list, and makes a point to the list:

a ----> [1, 2, 3]
  • The second line makes a point to the None object:
    [1, 2, 3]
    a ----> None
  • Since there's no way to get to the list or the objects it contains, they are all freed.
  • Objects and methods

    Because BASIC has only a few types, it tends to represent everything else with numbers. File handles are an example; window handles another. Python tends to create new types for everything.

    We have already seen a few types. You can use a type like a function, to create a new object of that type. Eg:

    >>> a = int("4")
    >>> a
    4
    >>> b = float(4)
    >>> b
    4.0
    >>> c = str(4)
    >>> c
    "4"
    >>> d = list()
    >>> d
    []

    BASIC has a global selection of functions, and typically prefixes the names with types to avoid clashes. Consider:

    account% = FNaccount_new()
    PROCaccount_credit(account%, 100)
    PRINT "Now has", FNaccount_check(account%)
     
    door% = FNdoor_new()
     
    PROCdoor_close(door%)
    PROCaccount_close(account%)

    Because python keeps track of types (and allows new ones to be created), it can automatically work out which of several functions to use, by keeping the functions 'inside the type'. Such functions are called 'methods':

    account = Account()
    account.credit(100)
    print "Now has", account.check()
     
    door = Door()
     
    door.close()
    account.close()

    Here 'Door' and 'Account' are types (like 'int' or 'str'), while 'account' and 'door' are values of that type (like 4 or "Hello"). Notice how we use the type to create new objects ('instances') of that type.

    Python knows that 'door' is a Door, so 'door.close()' calls the close function for doors, whereas 'account.close()' calls the close function for accounts.

    Most objects in python have many methods. Here are a few for strings:

    >>> a = "Hello"
    >>> a.upper()
    "HELLO"
     
    >>> a.lower()
    "hello"
    >>> "   spaces    ".strip()
    "spaces"
    >>> a = "Hello,Bye,Fred"
    >>> print "String contains", a.count(","), "commas"
    String contains 2 commas

    • Strings are immutable, so the above methods return a new string, leaving the original alone. In mutable objects, such as lists, some methods will modify the object in place.

    While we're on the subject of strings, I should point out that you can use either single or double quotes, as desired:

    >>> a = "Hello"
    >>> a = 'Hello'
     
    >>> a = '"Hello", she said.'
    >>> a = '''In python, " and ' can be used to quote strings''' 

    Tripple quotes of either type can also be used, and these can span multiple lines. Strings can be any length (unlike in BASIC); you can read an entire file into a string if you want.

    CASE ... OF

    There is no CASE statement. You can use IF for a direct BASIC translation:

    if x == 1:
            print "One"
    elif x == 2:
            print "Two"
    elif x == 3:
            print "Three"
    else:
            print "Many"

    However, as with FOR loops, you'll generally find that python makes such things unnecessary. Consider:

    FOR creature_number% = 0 TO number_of_creatures% - 1
      CASE type_of%[creature_number%] OF
        WHEN 1: PROCmove_elf(creature_number%)
        WHEN 2: PROCmove_goblin(creature_number%)
        WHEN 3: PROCmove_dragon(creature_number%)
        ...
      ENDCASE
    NEXT creature_number%

    would likely be written in python as:

    for creature in creatures: creature.move()
    • Create a new type for each kind of creature. Python will call
      the correct 'move' function for each creature automatically!

    ERROR

    ERROR 0, "User error!"

    becomes

    raise Exception("User error!")

    Exception is a type, so this creates a new Exception object and raises it as an error. It can be caught in a try block, as shown above. When you know how to create your own types, you can use that to create different types of error (any object can be raised as an error, even a string, but there are conventions...).

    OPENIN, OPENOUT

    > stream% = OPENIN("MyFile")
    > PRINT stream%
    254

    becomes

    >>> stream = file("MyFile")
    >>> stream
    <open file 'MyFile', mode 'r' at 0x8151290>

    • 'file' is a type, like 'str' or 'int'.
    • 'r' means 'for reading'
    • The number is a unique ID for the python object, so you can see
      at a glace whether it's the same as some other object.

    Note that 'stream' is not a string; that's just python's way of
    representing a 'file' object when you try to print it.
    You can use the file type's methods to read from it:

    >>> stream.readline()
    'The first line\n'
    >>> stream.readline()
    'The second line\n'
    >>> stream.readline()
    'The end\n'
    >>> stream.readline()
    ''

    Each line read includes the newline character, so you can tell the
    difference between a blank line ('\n') and the end of the file
    ('').
    More commonly, you'll use a file object as the sequence in a
    list. In this case, the loop iterates over the lines:

    my_data = file('MyFile')
    for line in my_data: print "Next line is", line
    my_data.close()

    Or, indeed:

    for line in file('MyFile'): print "Next line is", line
    • When the loop finishes, the file object is unreachable (since we never assigned it to a variable). Therefore, the garbage collector frees it, closing the file for us automatically.
    • Earlier, I said that for loops take a list to work on. In fact, a for loop takes an iterator (anything that can generate a sequence of values). Therefore, lists, files and even user-defined types can be used in for loops.

    For writing, pass 'w' to the constructor (the type used to create a new object):

    stream% = OPENOUT("Output")
    PRINT#stream%, "Hello World"
    CLOSE#stream% 

    becomes

    stream = file('Output', 'w')
    print >>stream, "Hello World"
    stream.close()

    Note that print's syntax is slightly unusual for python, as it's a statement rather than a function. I've shown this system as it's the most BASIC-like. You can also use the write method of the stream object if you prefer:

    stream.write("Hello World")

    LIBRARY

    >>> import math
    >>> math.cos(0)
    1.0
    • import loads routines from another file (called a 'module')
    • You access a module's functions using the same notation as an object's methods. This avoids name conflicts.

    For example, most math functions exist in both normal and 'complex' forms:

    >>> import cmath, math
    >>> math.cos(0)
    1.0
    >>> cmath.cos(0)
    (1-0j)

    You can also pull in some names directly:

    >>> from math import cos, sin
    >>> cos(0)
    1.0
     
    >>> sin(0)
    0.0

    You can use import to split your own programs into several files. Name the files with a '.py' extension, and use import without the extension to load the file.

    HELP

    Use the help function to find out about anything in python. You can get help on functions, types and modules!

    >>> import math
    >>> help(math)
    >>> help(math.cos)
    >>> help(str)

    Notice how we are passing apparently abstract ideas as function arguments! In fact, all these things are real python values, and can be assigned just like anything else:

    >>> import math
    >>> print math.cos(0)
    1.0
    >>> print math.cos
    <built-in function cos>
    >>> print math
    <module 'math' from '/usr/lib/python2.2/lib-dynload/math.so'>
    >>> a = math.cos
     
    >>> a(0)
    1.0

    AND, OR, EOR, ^

    Most python operators are the same as in BASIC. The bit-wise operators are different, however. EOR is written ^ in python, AND as & and OR as | (and ^ becomes **):

    > PRINT 6 AND 3
    2
    > PRINT 6 OR 3
    7
    > PRINT 6 EOR 3
    5
    > PRINT 6 ^ 3
    216

    becomes

    >>> 6 & 3
    2
    >>> 6 | 3
    7
    >>> 6 ^ 3
    5
    >>> 6 ** 3
    216

    BASIC also uses the bitwise operators AND and OR for boolean
    operations. Use the normal python 'and' and 'or' operators for
    this. These are 'short-circuit' operators; they only evaluate as
    far as they need to to get the result:

    >>> 0 and 1/0
    0

    There is no error, because python only evaluates as far as the 0
    before realising the result is going to be false.

    LOCAL

    Python doesn't have LOCAL; any variable assigned to in a function
    is automatically considered to be a local variable. Instead, the
    keyword 'global' must be used to indicate that a variable is NOT
    local:

    >>> def inc_a():
    ...     global a
    ...     a += 1
    ...
    >>> a = 1
    >>> inc_a()
    >>> print a
    2

    Also, the scoping rules are different. This isn't much of a
    problem, because python does what you'd expect, and BASIC doesn't.
    BASIC fakes local variables by storing the current global value on
    the stack and then restoring it when the function exits. Python
    does it properly. Consider:

     10 a% = 1
     20 PROCfoo
     30 END
     40
     50 DEF PROCfoo
     60 LOCAL a%
     70 a% = 2
     80 PROCbar
     90 ENDPROC
    100
    110 DEF PROCbar
    120 PRINT a%
    130 ENDPROC

    This BASIC program prints 2, perhaps rather surprisingly.

    def foo():
            a = 2   # Local variable
            bar()
     
    def bar(): print a
     
    a = 1
    foo()

    The python version prints 1, because the 'a' assigned in foo() is
    only accessible from within foo().
    To be picky, when you use a variable without assigning to it,
    python uses the variable assigned to in the closest enclosing
    lexical scope. Which only matters if you're doing un-BASIC things,
    like defining functions inside other functions. And it still does
    what you'd expect:

    def foo():
            a = 2   # Local variable
            def bar():
                    print a
            bar()
    a = 1
    foo()

    This prints 2, as you probably guessed.

    Future reading

    That's enough for now. Now play for a bit, and then read the
    offical python
    tutorial
    !
    A few other interesting bits to look out for in python:

    • The % operator can be used with a string to insert formatted
      values. Eg:
      >>> "Pi is %.3f (to three decimal places)" % math.pi
      'Pi is 3.142 (to three decimal places)'
    • The 'class' keyword lets you create your own types. Vital to
      take advantage of python properly!
      >>> class Point:
      ...     def __init__(self, x, y):
      ...             self.x = x
      ...             self.y = y
      ... 
      >>> p1 = Point(3, 4)
      >>> p2 = Point(7, 8)
      >>> p1.x
      3
      >>> p2.y
      8
    • There is a very useful `dict' type. It holds a set of elements,
      each with a `key' and a `value' and can look up a value from a key
      in constant time (no matter how big the dictionary gets). Very
      useful.
    • The 'assert' statement checks that something is true, and
      raises an exception if not. Sprinkle generously around your code to
      check for programming errors.
    • Everything is an object! Functions, modules, classes can all be
      assigned to variables just like other values. You can pass a
      function to another function, or get one back. Consider this:
      >>> def double(x): return 2 * x
      ...
      >>> map(double, [1,2,3])
      [2,4,6]

    :O Wow, Thanks alot. I first

    :O Wow, Thanks alot. I first started programming in QBasic just desided recently to pick up Python so this was a great bridge. :P

    Syndicate content