Can't figure out where None comes from

I’m playing around with exercise 44 and multiple inheritance.
I’ve written the following code to try to figure out what’s going one. However I two a None’s in my output, I can’t figure out where comes from?


class GreatGrandParent(object):
    def genes(self):
        print('Blue eyes')
        self.height = 73
        self.material = 'Crusty skin'

class GrandParent(GreatGrandParent):
    def genes(self):
        self.material = 'Old skin'
        self.height = 56

class Beauty(GrandParent):
    def genes(self):
        print(">>>BEAUTY")
        self.material = 'Skin'
        print("<<<BEAUTY")

    def cloth(self):
        print('Dress')

class Beast(object):
    def genes(self):
        print('>>>BEAST')
        self.material = self.magic()
        print('<<<BEAST')

    def magic(self):
        return 'Magic'

class Child(Beast, Beauty): #can only use multiple inheritance if arguments are 'unrelated'
    def Fairytales(self):
        print("I'm Mary")
        pass


dad = Beast()
mother = Beauty()
mary = Child()

print(mary.genes()) #Output >>>BEAST
print(">>> BETWEEN PRINT")
print(mary.material) #Output 'magic' becuase beast comes first and overrides beauty
print(mary.magic())
print(mary.cloth())

#print(mary.heigth) #does not work, because beauty genes overides

Output:

>>>BEAST
<<<BEAST
None
>>> BETWEEN PRINT
Magic
Magic
Dress
None

Hello @ktrager

I try to explain like this:

def uno():
    return "first function" 

# Just calling this function did not give me the string..
uno()

# print the function gives the string I return.
print(uno())
# >> first function

def dos():
    print("second function")

# Calling the function gives the print statement
dos()
# >> second function

# print the function gives None
print(dos())
# >> second function
# >> None

I have no deeper knowledge of this so far. I have noticed it works this way.

I saw that if you change

print(mary.cloth())
to
mary.cloth()

you will get rid of None

2 Likes

@ulfen69 is correct. You are mixing things up here a little @ktrager.

Remember you define (def) functions as actions that a class has. So a person may ‘walk’ or ‘talk’.

A person also has attributes, like age, hair colour, or skin-quality :slight_smile: as per your example.

You are defining these attributes as functions which adds a layer of complexity (the dot notation) to what you are doing. A person cannot ‘do’ skin type. They ‘have’ a skin type.

As Zed suggested, drill ‘is-a’, ‘has-a’.

I strongly suggest going back to the beginning of OOP section and drilling much more. Getting into multiple inheritance and it’s problems without a strong understanding of classes and methods will cause you a lot of pain. It will be worth the effort when it all clicks.

1 Like

These replys are great information.

I played with your code for a bit, and figured out that those 'None’s represent that your ‘gene()’ functions have nothing to return. If you added a return to each of these gene() functions, maybe like:

beast:
return ‘beast gene call’
beauty
return ‘beauty gene call’
IN beauty.cloth()
return “CLOTH”

ect…
you will see what that None disappear.
That is what these None’s are. You are asking to print a function that has no return. You have print functions in those function calls, that will print, but there is no ‘return’ from them, therefore it gets ‘none’.

print is looking for a return from what you put in those print(). A function call is always going to be run when it is in code first, kind of like order of operations. So you could do First_call(Second_call(Third_call()))) and it will run the calls in the order:
Third_call, Second_call, First_call.

1 Like

I believe @nellietobey has the right answer, and @ktrager I believe you’ve had this problem before. Correct me if I’m wrong, but didn’t you have a few problems that were because you failed to do a return in a function and then wondered why you get None from a function call?

If that’s true, then there’s this wonderful magic about being wrong:

If you’re consistently wrong, then you just need to realize that and you’ll be consistently right.

For example, if you are always going North because you think it’s South, then you just have to remember that, and every time you go “South”, just turn around and you’ll go the right way.

If, every time you write a function, you fail to return, then you know you are consistently wrong when you right a function. Now, all you have to do it remember that, and you will then consistently write a correct function by enforcing a rule to always add a return.

2 Likes

Hi @ulfen69, @gpkesley @nellietobey thank you for coming to the rescue again.

I think I understand, basically print(dos()) prints the whole function.
whereas dos() just runs the function with a print statement inside that returns whatever it’s printing.

But as @gpkesley suggested there is clearly something im missing.
I’ve done the is-a and has-a drilling. But might be me who is not getting it.
Please correct me if I’m wrong:
is-a is when something is inherited from another and has-a is something that’s specific to that class.

#Fish has-a __init__ 
#Fish has a food fucntion
class Fish():
    def __init__(self):
        self.fin = 4

    def food(self):
        return 'Fish food'

#Salmon is-a fish
#Salmon has-a name
class Salmon(Fish):
    def salmonproperties(self):
        self.name = 'None'


fishpet = Salmon()
fishpet.name = 'Nemo'

print(fishpet.name)
#Return: Nemo

print(fishpet.food)
#<bound method Fish.food of <__main__.Salmon object at 0x102c10518>>

fishpet.food
#Returns: nothing

How would I fx print the inherited return from the food funciton in the Fish class.
Or is it me who is completely getting this unnecessary twisted…?

Thank you for your patience.

I need to work on being consistently wrong :wink:

Yes, you are right i had this type of problem disguised over here.
I might have to go back and spend some time on the previous exercises.
OOP is so frustrating - I feel im getting it, and then at the same time not at all.

@ktrager the “has-a” and “is-a” really helps in understanding composition and inheritance at the object level.

A car “has-a” steering wheel, seats and an engine. The car is composed (composition) of unique parts. In your car model you can have classes for each of those things, all with their own functions, like “turn” or “autocorrect” for the steering wheel, or “run” for the engine.

However, a Ford is a type of car, so it can inherit some aspects of a car in your model, like number of wheels, colour, fuel type. It might be efficient to create a parent class for ‘car’ and inherit those things into instances of it, like Ford, Buick, McLaren, etc. The actual examples of the car are different, but they are all cars.

So the different of these two can works at class level and is a design consideration. You are correct in highlighting that anything inside a class belongs to it (this is its scope). But a class “has-a” ‘init’ is not quite right. It is true, (as all Python modules have an init under the covers) but this is not the intention of the exercise. It’s about thinking ‘objects’ and recognising patterns in similar Python syntax that means vastly different things.

1 Like

I have a blog where I work on problems like this, if your interested I wrote one with code you can play with that compares Python inheritance and attributes. It may help a bit. My blog is not monotized, no adds, and no profit, it’s just there to use.

2 Likes

Thanks again @gpkesley I think I’ll go a step back and play with the previous exercise.

I followed along with @nellietobey blog post and coded along with the examples and I do understand it when I follow along other code, but as soon as the ‘support wheels’ comes off it gets messy.

But guess it’s the mess I have get through to understand.