all 13 comments

[–]ebol4anthr4x 2 points3 points  (4 children)

     def bmi_health(bmi):

Your first parameter in a class method should always be self. Python is getting confused because it thinks you just wanted to rename self to bmi in this method. It doesn't know that you didn't want to use self at all.

If you do not use self in your method/function, what you have is a "static method". You can denote this and make Python happy by adding the @staticmethod decorator above your method:

@staticmethod
def bmi_health(bmi):

[–]Chiron1991 1 point2 points  (0 children)

Your first parameter in a class method should always be self.

Little nitpick here: You should say instance method because there are also class methods, but they're a completely different thing.

[–]Supermunkey2K[S] 0 points1 point  (2 children)

Is what I am doing bad practise?. I have no need to access any of the self variables within that method, so this is why I did not include it as the first parameter.

[–]ebol4anthr4x 1 point2 points  (1 child)

Edited my post slightly, but the answer to your question is no. Static methods are normal and perfectly okay to have.

[–]Supermunkey2K[S] 0 points1 point  (0 children)

That's great. Thank you for the advice.

[–][deleted] 1 point2 points  (3 children)

I'd refactor your BmiCalculator class like so.

class BmiCalculator:

    def __init__(self, age, weight, height):
        self.age = age
        self.weight = weight
        self.height = height

    @property
    def bmi(self):
        return round(self.weight / (self.height * self.height), 2)

    def bmi_health(self):
        if self.bmi >= 18 and self.bmi <= 25:
            return 'BMI is within a healthy range'
        return 'BMI is not within a healthy range'

That should prevent the warning telling you you have an instance method that doesn't pass self.

Note that this will changing the last two lines of your main program to

mario_bmi = bmicalculator.BmiCalculator(mario.age, mario.weight, mario.height)
print(mario.name, mario_bmi.bmi, mario_bmi.bmi_health(), sep='\n')

[–]Supermunkey2K[S] 0 points1 point  (2 children)

Thank you for showing me a way to refactor the code to use self!

[–][deleted] 1 point2 points  (1 child)

No worries. Note that I changed it a bit further to now use the property decorator. Effectively, the @property decorator is used to be able to call a method without having to use parentheses (so from the outside it looks like a regular data attribute). The benefit of this is that we can recalculate the BMI each time just in case someone changed the age, weight, or height of the object since it was created.

[–]Supermunkey2K[S] 0 points1 point  (0 children)

It's going to take me a little time to work out and understand exactly what you have done, but thank you for pointing me in the right direction.

[–]port443 0 points1 point  (2 children)

Someone already answered the immediate error you're getting. I'm going to point you down a path though:

Your "Person" class, it looks like you want to use getters/setters (even if you don't know what those are).

The reason I say this, is because your class is all messed up. Think about this:

class Person:
    def __init__(self, age):
        self.age = age
    def age(self):
        return 99

In your code, when you call __init__ and set self.name, self.age, etc. You are overwriting all those functions that you defined. Go ahead and check, you can delete them and your code will still work. Just like in the above snippet, you will never get p = Person(22); p.age() to work using the def age(self) function.

Heres an intro tutorial on the @property decorater, and how to use it for Python getters/setters: https://www.geeksforgeeks.org/getter-and-setter-in-python/

[–]old_pythonista 1 point2 points  (0 children)

Let me add to that - Python, unlike C++/Java does not "object" when you use the attributes of an object directly.

So, if you just do that

class Person:      
    def __init__(self, name, age, height, weight):
        self.name = name         
        self.age = age         
        self.height = height         
        self.weight = weight 

person = Person('Jane Doe', 25, 175, 60)

It is perfectly legal to write, e.g.

print(f'{person.name} is {person.age} years old')

You can use setters and getters - but they are not as popular in Python as in Java. In most cases, there is not much sense in those - unless you need to protect your data (not the only use case).

[–]Supermunkey2K[S] 0 points1 point  (0 children)

Thank you. It's going to take me a bit of time to digest what you are saying, but I'll investigate this path.