In an effort to code in a more python and OOP-style way, I wonder if anyone could advise me on implementing this concept please.
Let's say I have a base class for fruit, say apple and banana, which contains basic properties for the class, e.g. color. Then I want to class which inherits from fruit called juice, which adds methods and properties, e.g. volume, sugar.
A rough structure might be:
class Fruit(object):
def __init__(self, fruit_name):
if fruit_name == 'apple':
self.color = 'green'
self.sugar_content = 2
elif fruit_name == 'banana':
self.color = 'yellow'
self.sugar_content = 3
Then I inherit the methods of my Fruit classes:
class Juice(Fruit):
def __init___(self, fruit_name, volume, additives):
PERHAPS I NEED TO USE A SUPER STATEMENT HERE TO PASS THE fruit_name parameter (which is 'apple' or 'banana' BACK TO THE FRUIT CLASS? I want this class to be able to access sugar_content and color of the the fruit class.
self.volume = volume
self.additives = additives
def calories(self, sugar_added):
return sugar_added + 2*self.sugar_content* self.volume # i.e. some arbitrary function using the class parameters of both this juice class and the original fruit class
So ultimately, I can create an object like:
my_juice = Juice(fruit_name='apple', volume=200, additives='sugar,salt')
print 'My juice contains %f calories' % self.calories(sugar_added=5)
print 'The color of my juice, based on the fruit color is %s' % self.color
ALTERNATIVELY, I wonder if it is better NOT to inherit at all and simply call the fruit class from a Juice class. e.g.
class Juice(object):
def __init__(self, fruit_name, volume, additives):
self.fruit = Fruit(fruit_name=fruit_name)
self.volume = volume # ASIDE: is there an easier way to inherit all parameters from a init call and make them into class variables of the same name and accessible by self.variable_name calls?
self.additives = additives
def calories(self, sugar_added):
return sugar_added + 2*self.fruit.sugar_content* self.volume
In some ways, this above feels more natural as self.Fruit.sugar_content directly indicates the sugar_content is a property of the fruit. Whereas if I inherited, then I'd use self.sugar_content, which is a property of the fruit although could be confused with the sugar content of the juice class which is dependent on other factors.
OR, would it better still to have a separate class for each fruit, and put the logic statement to evaluate the fruit_name string passed to the Juice class witin the Juice class init and then use e.g.:
class Apple(object):
self.color = 'green'
class Banana(object):
self.color = 'yellow'
class Juice(object):
def __init__(self, fruit_name, other params):
if fruit_name == 'apple':
self.Fruit = Apple
self.sugar_content=2
elif fruit_name == 'banana':
self.Fruit = Banana
self.sugar_content=3
# Or some easier way to simply say
self.Fruit = the class which has the same name as the string parameter fruit_name
I appreciate all of the above would work in theory, although I'm looking for suggestions to develop an efficient coding style. In practice, I want to apply this to a more complex project not involving fruit, although the example encompasses many of the issues I'm facing.
All suggestions / tips / suggest reading links welcomed. Thanks.
I think your first option is closest to a good idea, but here's an improvement:
class Fruit:
def __init__(self, name, sugar_content):
self.name = name
self.sugar_content = sugar_content
Now the logic for determining how much sugar each Fruit
contains is moved outside the class definition entirely. Where can it go? One option would be to sub-class:
class Apple(Fruit):
def __init__(self):
super().__init__("apple", 2)
Alternatively, if the only differences between the Fruit
s are name and sugar content, just create instances and feed in the data (e.g. from a file):
apple = Fruit("apple", 2)
Now how about the Juice
. This is composed of Fruit
, but isn't itself actually Fruit
, so inheritance isn't a good fit. Try:
class Juice:
def __init__(self, fruits):
self.fruits = fruits
@property
def sugar_content(self):
return sum(fruit.sugar_content for fruit in self.fruits)
You can pass an iterable of Fruit
instances to make a Juice
:
>>> mixed_fruit = Juice([Fruit("apple", 2), Fruit("banana", 3)])
>>> mixed_fruit.sugar_content
5
You can then extend this to encompass ideas like:
Fruit
goes into the Juice
;Juice
is (another @property
calculated from the ingredients?); andIt would be sensible to work in consistent proportional units, e.g. sugar_content
in grams of sugar per gram of Fruit
(i.e. percentage).
Another way to think about this is with three entities Fruit
, Juice
, and Juicer
. The Juicer
is responsible for taking a certain Fruit
and creating a Juice
of the corresponding type Orange => OrangeJuice
. Then you could have two hierarchies of classes with Orange
inheriting from Fruit
and OrangeJuice
inheriting from Juice
with Juicer
essentially mapping between them.
This gets you around a lot of tricky problems that your proposed solution would generate. For instance, if I had a class called Slicer
that consumed some Fruit
produced a FruitSalad
(Fruit[] => FruitSalad
). In your proposed abstraction I could give Slicer
an OrangeJuice
instance (which you propose as a type of Fruit
) and expect it to spit out a delicious FruitSalad
but instead all I get is a soggy counter top.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With