How to get the number of input arguments of class(__init__)?

Viewed 173

I want to make the class score in python that takes each subject's score and return its average and sum. I know if I receive 4 arguments than denominator of average should be 4, but how I can make it to fixed codes that changed by number of inputs?, not just count numbers and type 4. I tried Len(self) or using for loop to count but Len makes error and for loop isn't that easy.

class score:
    def __init__(self, language, math, english, science):
        self.language = language
        self.math = math
        self.english = english
        self.science = science
        
    ...

    def sum_scores(self):
        result = self.language + self.math + self.english + self.science
        return result
    
    def average(self):
        average = self.sum_scores()/4 # Here is the problem!
        return average

This is my first question on stack. so sorry for my poor English and stupid questions.

4 Answers

Don't use 4 separate attributes in the first place. Use a dict to store the attributes; then you can query the size of the dict.

class score:
    def __init__(self, language, math, english, science):
        self.scores = {'language': language, 'math': math, 'english': english, 'science': science}
        
    ...

    def sum_scores(self):
        return sum(self.scores.values())
        
    
    def average(self):
        return self.sum_scores() / len(self.scores)

If you still want attributes for each individual score, use properties:

class score:
    def __init__(self, language, math, english, science):
        self.scores = {'language': language, 'math': math, 'english': english, 'science': science}
        

    @property
    def language(self):
        return self.scores['language']

    # etc.


    def sum_scores(self):
        return sum(self.scores.values())
        
    
    def average(self):
        return self.sum_scores() / len(self.scores)

For your case, you could just note that the quantity is 4

However, you may want to use **kwargs as an argument (you can use any name, this is just convention; ** assigns all the keyword args not specifically named to a dict) instead

SUBJECTS = ("math", "science" ... )

class MyClass():
    def __init__(self, **kwargs):  # also consider *args
        self.subjects = {k: v for k, v in kwargs.items() if k in SUBJECTS}

    def sum_scores(self):
        return sum(self.subjects.values())
    
    def average(self):
        return self.sum_scores() / len(self.subjects)
>>> c = MyClass(math=10, science=8)
>>> c.sum_scores()
18
>>> c.average()
9.0

You may also want to consider reporting on arguments that are not used to help users find usage errors

Assuming that it is OK that you don't know the subjects for which the scores apply, you can just use unnamed positional arguments

class score:
    def __init__(self, *args):
        self.scores = args
        
    ...

    def sum_scores(self):
        return sum(self.scores)
    
    def average(self):
        return self.sum_scores() / len(self.scores)

You can use keyword arguments in your init function:

class Score:
     def __init__(self, **scores):
         self.classes = []
         for _class, score in scores.items():
             setattr(self, _class, score) # self.english, self.math, etc.
             self.classes.append(_class) # ["math", "english"]
     def sum_scores(self):
         sum = 0
         for i in self.classes:
              sum += getattr(self, i)
         return sum
     def average(self):
         return self.sum_scores() / len(self.classes)

By using **scores, you can dynamically iterate over all arguments passed into the function as a dictionary. Here, I use the setattr function to give the Score object a property of its class name, and its value is the corresponding value from the dictionary. That way, I can dynamically iterate over each class, whether 3 or 100 classes are provided as input.

score1 = Score(math=67, english=98, art=76)

print(score1.sum_scores())
print(score1.average())
>>> 241
>>> 80.3333333333
Related