0

我正在尝试在 python 3 中学习面向对象的编程。我正在制作我在书中的笔记本程序的变体,但我没有向笔记本添加笔记,而是试图在时间表中添加天数。

在原始教程中,这是在主程序中:

def add_note(self):
    memo = input("Enter a memo: ")
    self.notebook.new_note(memo)
    print("Your note has been added")

这是在课堂模块(笔记本)中:

def new_note(self, memo, tags = ''):
    '''create a new note and add it to the list'''
    self.notes.append(Note(memo,tags=''))

我的变体如下所示:

主要的:

def add_work_day(self):
        date = input ("Enter date : ")
        hours = input ("Enter hours worked : ")
        rate = input ("Enter hourly rate : £")
        workday = Timesheet.day(date, hours, rate)

模块:

class Timesheet:
       def __init__(self):
            self.timesheet = []

        def day(self, date, hours, rate):

        self.timesheet.append(day(date, hours, rate))

它给了我这个错误:

File "C:\Python33\timesheet_menu.py", line 39, in add_work_day
    workday = Timesheet.day(date, hours, rate)
TypeError: day() missing 1 required positional argument: 'rate'

似乎'def day(self,date,hours,rate)'中的'self'占用了我的输入参数之一。

有人能告诉我我在这里想念什么吗?

.....更新.....

所以现在我在 main 中创建了一个 Timesheet() 实例:

    def add_work_day(self):
        date = input ("Enter date : ")
        hours = input ("Enter hours worked : ")
        rate = input ("Enter hourly rate : £")
        workday = Timesheet()
        workday.add_day(date, hours, rate) 

但是我的 Timesheet() 方法“day”出现了一个新错误

class Timesheet:


    def __init__(self):
        self.timesheet = []

    def day(self, date, hours, rate):
        self.timesheet.append(day(date, hours, rate))

File "C:\Python33\timesheet_menu.py", line 40, in add_work_day
workday.add_day(date, hours, rate)
File "C:\Python33\timesheet.py", line 29, in add_day
self.timesheet.append(day(date, hours, rate))
NameError: global name 'day' is not defined

我知道问题出在 .append(day 部分,但我不知道如何解决它。我知道一个变量不是全局的,除非指定,但我的逻辑告诉我该方法应该是。所以它必须是.append(day 正在寻找一个名为“day”的预先存在的变量。我很困惑,因为这种方法在书中的示例中有效。

4

3 回答 3

2

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.

于 2013-04-28T04:04:02.463 回答
1

更改workday = Timesheet.day(date, hours, rate)workday = Timesheet().day(date, hours, rate)

于 2013-04-27T22:45:17.883 回答
0

您遇到的问题是您没有Timesheet在当前代码中创建实例。你只是直接在类上调用一个方法。由于未绑定的方法只是函数,因此您会收到参数不匹配错误,因为没有self要隐式传递的对象。

这是我修复您的代码的方法:

def add_work_day(self):
    date = input ("Enter date : ")
    hours = input ("Enter hours worked : ")
    rate = input ("Enter hourly rate : £")

    workday = Timesheet() # create Timesheet instance
    workday.day(date, hours, rate) # add a workday to it

    # presumably you'll also want to do something with `workday` here too

现在,您可能已经Timesheet在您的方法所在的类的其他地方创建了一个实例。在这种情况下,您只需要引用它(通过self),而不是创建一个新实例:

def add_work_day(self):
    date = input ("Enter date : ")
    hours = input ("Enter hours worked : ")
    rate = input ("Enter hourly rate : £")

    self.myTimesheetInstanceCreatedSomewhereElse.day(date, hours, rate)
于 2013-04-28T02:16:52.823 回答