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.
Here's the familiar hello world program in BASIC and in Python:
PRINT "Hello World"
becomes
print "Hello World"
a = 4.5 b% = 3 c$ = "Hello World"
becomes
a = 4.5 b = 3 c = "Hello World"
d = None
number = input("Enter a number:")
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).
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
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]
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 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 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" ...
>>> def double(x): return 2 * x ... >>> double(4) 8
>>> def hello(): ... print "Hello World" ... >>> hello() Hello World
>>> a = hello() Hello World >>> print a None
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"
>>> a[1] = 4 >>> a ["red", 4, "blue"]
>>> a[-1] "blue" >>> a[-2] 4
>>> a[1:3] [4, "blue"] >>> a[1:] [4, "blue"] >>> a[:1] ["red"] >>> a[:-1] ["red", 4]
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'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"
In fact, python provides a better way to count, as we shall see later.
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"
try: answer = 1 / number except DivisionByZero: print "Can't devide by zero!" except: print "Something wierd happened!"
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).
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!
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
a ----> [1, 2, 3]
[1, 2, 3] a ----> None
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
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.
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()
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...).
> stream% = OPENIN("MyFile") > PRINT stream% 254
becomes
>>> stream = file("MyFile") >>> stream <open file 'MyFile', mode 'r' at 0x8151290>
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
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")
>>> import math >>> math.cos(0) 1.0
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.
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
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.
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.
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:
>>> "Pi is %.3f (to three decimal places)" % math.pi 'Pi is 3.142 (to three decimal places)'
>>> 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
>>> def double(x): return 2 * x ... >>> map(double, [1,2,3]) [2,4,6]