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]
[1, 2, 3] a ----> None
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]
- Printer-friendly version
- Login to post comments
: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