The root of your problem is that you don't yet understand how Python classes and instances work.
A class, like Timesheet
, is a collection of methods (functions) and variables, which live in the class's namespace. An instance is a specific instance of the class (i.e., this timesheet, as opposed to all the other timesheets that exist). Each instance has its very own namespace, which is slightly special: when you look for a method or variable in an instance namespace, if the name is not found, the class namespace will be searched next. (And if the class inherits from other classes, the namespaces of its ancestors will be searched, in order, until either the name is found or there are no more namespaces left to search.)
Now, methods (functions) defined in classes have a special behavior, which functions defined outside of classes don't have -- this is why a different term (methods) is used for functions defined in classes, to help remind you of this special behavior. The special behavior is this: if the function is being called on an instance of the class, then that instance will get passed as an "extra" first parameter to the function. (By convention, that first parameter is called self
, but there's no reason you couldn't call it fhqwhgads
if you wanted to. You shouldn't -- it would just make your code utterly confusing to read -- but you could if you wanted to.) Why that extra first paremeter? Well, remember how I said that instances have their own namespace? If you want to look up variables on the instance (e.g., you want to look up the entries on this timesheet, not that other timesheet over there), then you need a reference to the instance. The self
parameter provides that reference.
Now, if you call the methods on the class, as opposed to on an instance, there's no need for that extra self
parameter, because you clearly already have a reference to the class: that reference is the name Timesheet
. So when you do Timesheet.day(...)
, there will be no "extra" first parameter added before your other parameters. That's because you're not referencing an instance, you're referencing a class.
But if you call Timesheet().day(...)
, then two things are happening. First, you're creating an instance of Timesheet
(the Timesheet()
formulation is how you create an instance), and then you're calling the day()
method on that instance. So the "extra" first parameter will be passed to your day()
method so that your code inside day()
will be able to access that instance's variables.
One other thing you'll need to understand: when variables belong on an instance, and when they belong on a class. There's a very simple question you can ask yourself to determine this: "does this apply to every timesheet, or only to specific timesheets?" Your day()
method clearly needs to access values from specific timesheets (Joe worked different hours than Bob, at a different rate of pay), so you need to call it on instances, not on the class. So having a self
parameter in your day()
method makes sense, but you also need to call it from a method, not from the class.
So instead of Timesheet.day(...)
, you should do something like:
my_timesheet = Timesheet()
my_timesheet.day(...)
# Now do something with the timesheet: calculate total pay, print it out, etc.
my_timesheet.calculate_total_pay() # Made up example
my_timesheet.print_to_screen() # Made up example
It would make no sense to do Timesheet.calculate_total_pay()
, because the total pay depends on the values in specific, individual timesheets. So calculate_total_pay()
should also be an instance method, and should therefore have a self
parameter. Actually, in this case I'm not coming up with any methods that should be called as Timesheet.some_method()
. (Methods called like that are called "static methods" in Python, BTW). Every single example method I can come up with is an instance method (i.e., a method that should be called on an instance, because it would need to access data from that specific timesheet).
A bit long-winded, but I hope this helps you understand classes and instances better.