[Tutor] Understanding Object oriented programming with Python
Alan Gauld
alan.gauld at yahoo.co.uk
Fri Sep 4 18:43:53 EDT 2020
On 04/09/2020 20:17, Manprit Singh wrote:
> Please consider a problem of finding the factorial of a number, i have to
> solve this problem using OOPS concepts . I just want to check the
> understanding of my concepts through this program, which is written below .
Lets be clear what we mean by OOPs. It means Object
Oriented Programming. That means we program with objects.
Objects are instances of classes and they communicate
by sending messages to each other. Messages trigger
method invocations.
Building classes is a necessary precursor to object oriented
programming, but it is not OOP in itself. For OOP to be
effective we must first determine what the objects in
a program are. Objects have internal state and operations.
This brings us to the question of whether factorials
are objects? Do they have a state and operations?
If so what are they? Or is factorial really an operation
on a number? (like addition or multiplication?) In which
case number is the object and factorial a method. There is
no absolute correct answer to these questions, scenarios
can be found in which either option is valid.
These questions may seem like semantics but they are
fundamental to the difference between programming with
objects and object oriented programming.
> class factorial: # Defining class factorial
Its customary to use capitalised names for user defined classes. Thus
Factorial. Its purely a convention but one that goes back to the dawn of
OOP in the early 1970's!
> def __init__(self): # Initializer
> self.num = None # Initial value of the number
> self.fact = None # Initial value of factorial of the
> number
Given that this class is the factorial there is something wrong
I having an attribute called fact. It leads to a recursive definition.
The factorial of a factorial is???? Instead this should perhaps
be the *value* But do we need to store the value when the purpose of a
factorial is to be calculated? Maybe, if caching values leads to high
performance in our application. But usually not.
As for the initializer would it not be much more convenient to allow the
user to pass the number as an argument to init()? Then you can calculate
the value when first initialised.
def __init__(self, number = none):
if number:
self.number = number
self.value = self.calculate()
else:
self.number = None
self.value = None
> def setnum(self, x): # Setter , to set the value of self.num
> self.num = x
In Python we tend not to use setter and getter methods. We simply
grant public access. If we need to control access(read only or
to validate values) we prefer to use properties. Defining a property
is however significant extra work and requires knowlege of decorators
so we will ignore it here.
In this case if you want a setter it should do something useful,
like recalculate the factorial if the number is different:
def setnum(self, num):
if num == self.num: return
self.num = num
self.calculate()
> def factcalc(self): # Method that will calculate the
> factorial
> if self.num in {0,1}:
> self.fact = 1
> else:
> start = 1
> for i in range(2, self.num + 1):
> start = start * i
> self.fact = start
If the object is a factorial why would you include the object
type in the method name? calculate() should be sufficient.
And if we have already calculated the value we should not do
it again, so we should check that self.value is not None first:
def calculate(self):
if self.value: return
if self.num....as before
> def getfact(self): # Method that will return the factorial
> self.factcalc()
> return self.fact
You only want to calculate if its not already calculated.
And if you follow the advice above the value should always
be calculated if num is set. So you should only need to
return the value.
> fac = factorial() # Creating new instance of class and
> assign this object to a variable fac
> fac.setnum(3) # passing argument to setter(Argument is
Would be better as
fac - Factorial(3)
> print(fac.getfact()) # This will return the factorial of the
> number passed to setter, which is 3
>
> The output is 6
> Just need to know, if my understanding of basic concept is right or not.
> This problem can be improved further using OOPS ?
The real issue here is whether this is an appropriate use of OOP at all.
While I can foresee a scenario where a calculation like factorial might
be created as a class, it is very unlikely - maybe a calculator
that keeps track of past calculations. In which case Factorial would
be a subclass of the abstract Calculation class. But then we would
likely have many other methods for retrieving calculations,
displaying them along with the time executed etc. For a purely
math usage like this, factorial would normally just be a method
of a number class. Or, most likely in python, a simple function.
OOP is a powerful technique but it is not the best, or even an
appropriate, solution in every case. And writing a class does
not make a solution object oriented. One of the problems with
teaching OOP is that its value only becomes evident when
dealing with more complex examples that require multiple
classes, not just one. It is the interaction between objects
that makes a design object oriented, not the use of classes.
But of course you must learn how to build one class before
you can build many... That is the OOP paradox.
There is a very good PyCon video on YouTube somewhere called
something like "Stop writing classes" which makes the point that
a class with only one method (excluding getter/setter and init)
is really just a function. And if it has no useful methods its
just a data structure. It is a very important truth. I recommend
the video.
--
Alan G
Author of the Learn to Program web site
http://www.alan-g.me.uk/
http://www.amazon.com/author/alan_gauld
Follow my photo-blog on Flickr at:
http://www.flickr.com/photos/alangauldphotos
More information about the Tutor
mailing list