# Inheritance Hierarchy Example
# Python 3 Reflexive Metaprogramming
# H. Conrad Cunningham

# Developed for CSci 658, Software Language Engineering, Spring 2018

#234567890123456789012345678901234567890123456789012345678901234567890

# 2018-04-03: (V1)
# 2018-04-22: (V1a) Add type, isinstance, issubclass checks
# 2018-04-24: (V1b) Add __init__ to P

class P:
    def __init__(self,name=None):
        self.name = name
    def process(self):
        return f'Process at parent P level'

class C(P):   # class C inherits from class P
    def process(self):
        result = f'Process at child C level'
        # Call method in parent class
        return f'{result} \n  {super().process()}'
			
class D(P):   # class D inherits from class P
    pass

class G(C):   # class G inherits from class C
    def process(self):
        return f'Process at grandchild G level'

if __name__ == '__main__':
    p1  = P()
    c1  = C()
    d1  = D()
    g1  = G()
    print(f'p1.process()  -> \n{p1.process()}')
    print(f'c1.process()  -> \n{c1.process()}')
    print(f'd1.process()  -> \n{d1.process()}')
    print(f'g1.process()  -> \n{g1.process()}')
    obj = d1
    print(f'obj.process() -> \n{obj.process()}')
    obj = g1
    print(f'obj.process() -> \n{obj.process()}')

    # Added for V1a
    print("type checks within class hierarchy")
    print(f'type(P)                   = {type(P)}')
    print(f'type(C)                   = {type(C)}')
    print(f'type(G)                   = {type(G)}')

    print("issubclass checks within class hierarchy")
    print(f'issubclass(P,object)      = {issubclass(P,object)}')
    print(f'issubclass(C,P)           = {issubclass(C,P)}')
    print(f'issubclass(G,C)           = {issubclass(G,C)}')
    print(f'issubclass(G,P)           = {issubclass(G,P)}')
    print(f'issubclass(G,object)      = {issubclass(G,object)}')
    print(f'issubclass(C,G)           = {issubclass(C,G)}')
    print(f'issubclass(G,D)           = {issubclass(G,D)}')

    print("issubclass checks against type")
    print(f'issubclass(P,type)        = {issubclass(P,type)}')
    print(f'issubclass(C,type)        = {issubclass(C,type)}')
    print(f'issubclass(G,type)        = {issubclass(G,type)}')

    print("isinstance checks against type")
    print(f'isinstance(P,type)        = {isinstance(P,type)}')
    print(f'isinstance(C,type)        = {isinstance(C,type)}')
    print(f'isinstance(G,type)        = {isinstance(G,type)}')

    print("check type's relationships with object and itself")
    print(f'type(type)                = {type(type)}')
    print(f'issubclass(type,object)   = {issubclass(type,object)}')
    print(f'isinstance(type,type)     = {isinstance(type,type)}')

    print("check object's relationships with type and itself")
    print(f'type(object)              = {type(object)}')
    print(f'issubclass(object,type)   = {issubclass(object,type)}')
    print(f'isinstance(object,type)   = {isinstance(object,type)}')
