import py # ____________________________________________________________ # Example of building classes "manually", by calling type() A = type("A", (object, ), {}) # 'object' is the base class def f(a, z): return a.x + a.y + z A.f = f # 'f' is now a method in the class 'A' B = type("B", (A, ), {}) # B inherits from A def g(b): return b.x * 17 B.g = g # ____________________________________________________________ # Example of building equivalent classes by using the # normal syntax provided by Python class A2(object): def __init__(self, x, y): # constructor self.x = x self.y = y def f(self, z): return self.x + self.y + z class B2(A2): def g(self): return self.x * 17 # ____________________________________________________________ # Test and implement an equivalent of the built-in # issubclass() function: def test_issubclass(): assert my_issubclass(A, object) assert my_issubclass(B, object) assert my_issubclass(B, A) assert my_issubclass(A, A) assert my_issubclass(A, B) == False def my_issubclass(subcls, cls): while subcls is not None: if subcls is cls: return True subcls = subcls.__base__ # note that object.__base__ is None return False # ____________________________________________________________ # Test and implement an equivalent of the built-in # isinstance() function: def test_isinstance(): a = A() b = B() assert my_isinstance(a, A) assert my_isinstance(a, object) assert my_isinstance(b, B) assert my_isinstance(b, A) assert my_isinstance(b, object) assert my_isinstance(a, B) == False assert my_isinstance(a, list) == False assert my_isinstance(1.1, list) == False def my_isinstance(obj, cls): return my_issubclass(obj.__class__, cls) # ____________________________________________________________ # Sending messages to objects def test_send(): a = A() a.x = 12 a.y = 13 assert a.f(1) == 26 # the normal Python syntax assert send(a, "f", 1) == 26 # the equivalent call to our send() below b = B() b.x = 14 b.y = 15 assert b.f(-1) == 28 assert send(b, "f", -1) == 28 assert b.g() == 14 * 17 assert send(b, "g") == 14 * 17 def test_send2(): # The same as test_send(), but using the classes A2, B2 instead of A, B a = A2(12, 13) assert a.f(1) == 26 assert send(a, "f", 1) == 26 b = B2(14, 15) assert b.f(-1) == 28 assert send(b, "f", -1) == 28 assert b.g() == 14 * 17 assert send(b, "g") == 14 * 17 def test_send_message_not_found(): # when a method is not found, we get an exception AttributeError a = A() a.x = 12 a.y = 13 py.test.raises(AttributeError, "a.notthere(1)") py.test.raises(AttributeError, "send(a, 'notthere', 1)") # Remark: py.test.raises() tries to execute the piece of code given # as a string, and checks that it raises the specified exception def send(obj, message, *args): # 'message' is a string, looked up in the class of 'obj' cls = obj.__class__ while cls is not None: if message in cls.__dict__: # does the class have this method? method = cls.__dict__[message] # 'method' is really a function object: insert 'obj' as 1st arg return method(obj, *args) cls = cls.__base__ raise AttributeError(obj.__class__.__name__ + " object does not have attribute " + message)