Object Oriented Programming, to my understanding, is a adaptation of Conceptual
Cognition, just as Set Theory in mathematics is another adaptation. Details
aside, it's a branch of cognition in programming world.
A human brain, with its relatively high level of evolution, has
the ability of rational thinking, consisting of abstract and mathematical thinking.
From abstract thinking, we developed the concept "concept", then conceptual cognition.
From mathematical thinking came the concept "set", then set theory.
Originally, conceptual cognition is the way of a human brain to
understand objective reality, through which this person could better adjust to
the world and take advantage of it. By abstracting some common
features in a series of objects, one could form a concept,
then develop relative knowledge, resolution and reaction.
A similar process also happens when people deal with mathematical questions.
We abstract some shared features of several objects, and form a
set, like "the set of points that have the same distance
to a given point". Simply put, it's a sphere. With set
theory in mind, every problem we deal with is not restricted
to one object, but the solution is for all elements in
this set.
Then, there's object-oriented-programming. In OOP, we have "class" and "object". A
class is equivalent to a concept in conceptual cognition, or a
set in set theory, and of course an object is equivalent
to a real world entity, or a mathematical element.
In OOP, inheritance is a possible connection between two classes, if
one has the attributes all from the other, but with more
specific ones, which effectively makes this one a subclass, and the
other one a superclass. Subclass must have all attributes and methods
of its superclass, and the same rules of using them. Therefore
in every program every time an instance of the superclass is
applied, it should be replaceable by an instance of the subclass.
And doing this should not change the program's functionality, or throw
errors. Otherwise it can be sure that there are some flaws
in the designation of this program.
Similarly, in conceptual cognition, after the definition of a concept, if
some of the entities of this concept share some more specific
aspects, they can be further abstracted, and forms a sub-concept based
on the existing one. In any true proposition any occurrence of
the super-concept can be replaced with the sub-concept, and the state
of truth should not change. In set theory, this connection is
called "subset relation", and corresponding sets are called "superset" and "(proper)
subset". Back to the previous example, some points of the sphere
are located in a single plane, hence they form a subset,
"the set of points in one plane that have the same
distance to a given point". That is a circle.
There is a master concept in conceptual cognition, a super-concept to
all other concepts, a container of all entities, called "existence". There
is also a master set in set theory, a set that
contains all elements, called "universe" (when considering the whole mathematical world).
Its equivalence in OOP is called "base class". In Python, this
class is simply called "object". Whenever I need to define a
class (with no inheritance from another), I go this way:
Code Area 1
class Spaghetti(object):
def sayHi(self):
print ("[They don't speak.]")
The key here is the superclass name, "object". It can be
omitted, but its appearance here does reveal something important, that the
Spaghetti class is a subclass of object. This rule applies to
all classes, so it can be conclude that all classes are
subclasses of object.
(This is also a hint of materialism, but it's another story.)
So that's why OOP is convenient. It simulates how human brains
understand the world. Conversely, OOP is very helpful to learn Conceptual
Cognition, or Set Theory. I just need to know that everything
in this world is an object, and every object can be
abstracted into a concept. So is everything in mathematical world.
But there's one thing, served as the gravest, deadliest, most grievous
mistake in OOP, the overriding. Since a method of a superclass
is overridden by a subclass, the inheritance relation between the two
technically disappears. An instance of the superclass is not necessarily replaceable
by an instance of the subclass (depending on the implementation). Although
the mistake can be avoided by carefully specifying the types of
arguments, parameters and returning values, and strictly following these specifications, it's
an utterly extra burden. If needed, a new method must be
re-defined in the subclass, no matter how similar it is to
the corresponding method in the superclass. And the original one should
never be overridden, even if it's useless in the subclass.
As if for logical completeness, there's multiple inheritance. Similar to the
scenario in cognition, where a concept can be sub-concept of several
separated concepts that are not derived from each other, and in
mathematics, a set can be a subset of several different sets
that have no super-sub relation, a class can inherit from multiple
superclasses that don't inherit from each other.
Code Area 2
class Animal(object):
def __init__(self, *args, **kwargs):
self.objName = kwargs['objName']
self.isAnimal = True
self.animalStr = "%s is not a programmer." %(self.objName)
def getIsAnimal(self):
return self.isAnimal
def describeAnimal(self):
print (self.animalStr)
class Mammal(Animal):
def __init__(self, *args, **kwargs):
if kwargs['b_prop']:
# use b_prop to call every superclass __init__
# at the same time, avoid duplicated calls
kwargs['b_prop'] = False
for eachSuperClass in Mammal.__mro__[-2:0:-1]:
# reverse because a subclass could define attributes more accurately
eachSuperClass.__init__(self, **kwargs)
self.objName = kwargs['objName']
self.isMammal = True
self.mamStr = "%s indeed is a mammal." %(self.objName)
def getIsMammal(self):
return self.isMammal
def describeMammal(self):
print (self.mamStr)
class LandedAnimal(Animal):
def __init__(self, *args, **kwargs):
if kwargs['b_prop']:
kwargs['b_prop'] = False
for eachSuperClass in LandedAnimal.__mro__[-2:0:-1]:
eachSuperClass.__init__(self, **kwargs)
self.objName = kwargs['objName']
self.isOnLand = True
self.landedStr = "%s lives happily on land." %(self.objName)
def getIsOnLand(self):
return self.isOnLand
def describeOnLand(self):
print (self.landedStr)
class LeggedAnimal(LandedAnimal):
def __init__(self, *args, **kwargs):
if kwargs['b_prop']:
kwargs['b_prop'] = False
for eachSuperClass in LeggedAnimal.__mro__[-2:0:-1]:
eachSuperClass.__init__(self, **kwargs)
self.objName = kwargs['objName']
self.legCnt = kwargs['legCnt']
self.legStr = "%s indeed has %s leg(s)." %(self.objName, self.legCnt)
def getLegCnt(self):
return self.legCnt
def describeLeg(self):
print (self.legStr)
class DogPlus(Mammal, LeggedAnimal):
def __init__(self, *args, **kwargs):
if kwargs['b_prop']:
kwargs['b_prop'] = False
for eachSuperClass in DogPlus.__mro__[-2:0:-1]:
eachSuperClass.__init__(self, **kwargs)
self.objName = kwargs['objName']
self.greetingStr = kwargs['greetingStr']
self.farewellStr = kwargs['farewellStr']
def greeting(self):
print (self.greetingStr)
def farewell(self):
print (self.farewellStr)
macconnell = DogPlus(b_prop=True, \
objName="MacConnell", legCnt=4, \
greetingStr="Hi there sweety.", farewellStr="Till next time sweety.")
if macconnell.isAnimal:
macconnell.describeAnimal() # It moves too!
if macconnell.isMammal:
macconnell.describeMammal() # MacConnell is indeed a mammal.
if macconnell.isOnLand:
macconnell.describeOnLand() # MacConnell lives happily on land.
if macconnell.legCnt:
macconnell.describeLeg() # MacConnell indeed has 4 leg(s).
else:
print ("NO LEGS!")
macconnell.greeting() # Hi there sweety.
macconnell.farewell() # Till next time sweety.
Admittedly, its syntax is poorly designed. In practice, the statements are
confusing, error-prone and low-efficient. If you'd like to really dive into
it though, you would find it's not logically inadequate. It's just
not something I wanna do in production, which is another reason
why overriding is a bad idea. If I may say, 80%
problems in multiple inheritance are caused by overriding. And if not
for MRO, Method Resolution Order, this number could easily reach 99%.
Fortunately, multiple inheritance is probably only for logical completeness. And in
practice if I encounter an situation where multiple inheritance is needed,
I would rather look for an alternative, like Composition. The way
I see it, it's there because logical completeness weighs more than
pure production.
Finally I must say that this article is based on my
personal experience. There could be something technically wrong, but it's helpful
in my programming practice, and perspective of things. Should it be
helpful to you, I'm more than happy.
0 / 960