The 'quick course' should probably include more discussion of strong v. weak typing, and the strengths and weaknesses of each. There are good reasons for strong typing, even if it's not appropriate in every situation.
As others have pointed out above, the fact that the function f() can be called on objects of differing types is not an example of weak typing, but rather dynamic typing.
Python has some rather interesting examples of inherent casting methods, and will indeed throw type exceptions in certain cases. As an example of Python's type interactions, look at Mark Lutz' _Programming Python_, 2E, page 18:
>>> "42" + str(1), int("42") + 1
('421', 43)
Notice how Python handles the difference between string representations of a number and actual integers. Now try a slightly different line of code, as follows:
>>> int("42") + "1"
Traceback (most recent call last):
File "", line 1, in ?
int("42") + "1"
TypeError: unsupported operand types for +: 'int' and 'str'
Strong typing, not weak. But, as per your f() function, late-binding, dynamic typing.
Frankly, I find this quick review of classes to be a mishmash. So many selfs and methods and derivations and such.
I had a lot of troubles understanding the output from Simple2 until I changed the code. I don't see a big problem with the code being slightly different from what is printed:
#: c01:SimpleClass.py
class Simple:
def __init__(self, str):
print "Inside the Simple constructor of SimpleClass.py"
self.s = str
# Two methods:
def show(self):
print self.s
def showMsg(self, msg):
print msg + ':',
self.show() # Calling another method
if __name__ == "__main__":
# Create an object:
x = Simple("constructor argument")
print "Calling x.show from SimpleClass.py"
x.show()
print "Calling x.showMsg from SimpleClass.py"
x.showMsg("A message")
#
output = '''
Inside the Simple constructor
constructor argument
A message: constructor argument
'''
#: c01:Simple2.py
from SimpleClass import Simple
class Simple2(Simple):
def __init__(self, str):
print "Inside Simple2 constructor"
# You must explicitly call
# the base-class constructor:
Simple.__init__(self, str)
def display(self):
self.showMsg("Called from display()")
# Overriding a base-class method
def show(self):
print "Overridden show() method"
# Calling a base-class method from inside
# the overridden method:
Simple.show(self)
class Different:
def show(self):
print "Not derived from Simple"
if __name__ == "__main__":
x = Simple2("Simple2 constructor argument")
print
print "Calling x.display from Simple2.py"
x.display()
print
print "Calling x.show from Simple2.py"
x.show()
print
print "Calling x.showMsg from Simple2.py"
x.showMsg("Inside main")
def f(obj): obj.show() # One-line definition
print
print "Calling f from Simple2.py"
f(x)
print
print "Calling f(Different) from Simple2.py"
f(Different())
#
output = '''
Inside Simple2 constructor
Inside the Simple constructor
Called from display(): Overridden show() method
Simple2 constructor argument
Overridden show() method
Simple2 constructor argument
Inside main: Overridden show() method
Simple2 constructor argument
Overridden show() method
Simple2 constructor argument
Not derived from Simple
'''
http://www.yahoo.com, http://www.google.com, http://www.bing.com, http://www.aol.com, http://www.msn.com,