【问题标题】:Python: Same name for class method parameters and class attributePython:类方法参数和类属性同名
【发布时间】:2018-04-16 01:05:45
【问题描述】:

我有一个班级作业。我的任务之一如下:

一个。通过添加一个新属性 hunger 来扩充 Tribute 类,该属性将描述 对贡品的渴望程度。 hunger 的初始值应为 0,因为所有 Tributes 将满肚子地开始游戏。

b.创建一个方法,get_hunger(),返回贡品的当前饥饿度。

c。创建一个方法,add_hunger(hunger),它将为 Tribute 的值添加饥饿值 饥饿。当贡品的饥饿值等于或大于 100 时,他/她会 go_to_heaven()。 (仅供参考go_to_heaven() 之前由其他父类定义)

1) 我编写了以下代码,当我尝试运行它时,我不断在self.get_hunger()+=hunger 之前的缩进处突出显示语法错误。我可以知道语法错误的原因吗,因为.get_hunger() 本质上是self.hungerself.get_hunger()=0 将适用于此任务之后的其他代码,但我不明白为什么 self.get_hunger()+=hunger 不起作用。我的讲师强调不要破坏底层抽象层,这就是为什么我会使用方法.get_hunger() 而不是属性hunger,特别是如果我需要从 Tribute 的未来子类的实例中获取饥饿值,不确定这是否概念也在实际情况中被接受。

class Tribute(Person):
    def __init__(self, name, health):
        super().__init__(name, health, -1)
        self.hunger=0

    def get_hunger(self):
        return self.hunger

    def add_hunger(self,hunger):
        self.get_hunger()+=hunger  #dk why can't assign to function call
        if self.get_hunger()>=100:
            self.go_to_heaven()

2)我还尝试编写 self.hunger+=hunger 而不是 self.get_hunger()+=hunger 以克服语法错误并且它可以工作。但是,我不直观地发现为什么在定义类方法时以及当我面临以下情况时方法参数名和类属性名相同,参数不会覆盖hunger形式的属性。谁能跟我讲道理?

【问题讨论】:

    标签: python python-3.x class


    【解决方案1】:

    变量执行赋值。这就是 Python 的工作原理。变量是对内存中对象的引用。

    函数调用返回对象,你不能分配给一个对象。


    我建议使用 setter 方法来处理抽象的另一面。

    class Tribute(Person):
        ...
    
        def get_hunger(self):
            return self.hunger
    
        def set_hunger(self, hunger):
            self.hunger = hunger
    
        def add_hunger(self,hunger):
            self.set_hunger(self.get_hunger() + hunger)
            if self.get_hunger() >= 100:
                self.go_to_heaven()
    

    【讨论】:

    • @PrashinJeevaganth 不应该,我认为不会。我也没有在您的代码中的任何地方看到它。你改用self.get_hunger()==0了吗?
    • ok nvm,它不起作用,你知道我的第二个问题的解释吗?
    • @PrashinJeevaganth 绝对允许直接在类中引用实例属性,尤其是在 setter 方法中。 self.hunger += hunger 很好,如果您的教授不同意,请让他们来chat.*.com/rooms/6/python 见我们
    • 什么是setter方法,什么场景下不允许引用实例属性?到目前为止我一直在处理子类和父类(可能是5个类的层次结构),如果最高层次的父类没有adder方法,我想我仍然必须参考父类的属性在子类中。
    • 对于你的set_hunger(self,hunger),我们可以用self.hunger+=hunger代替吗,那么当我们在add_hunger中调用set_hunger时,我们可以将参数设置为hunger而不是self.get_hunger() + hunger。我从你的观点来看,应该提示我为一个类编写某种 set_hunger 函数,这样子类就不必触及父类的属性。
    【解决方案2】:
    1. 看起来您已经有了抽象,因为您正在使用一种方法来增加类字段add_hunger() 并在内部进行健康检查。不直接在自己的方法中使用类字段似乎没有多大意义。

    2. 您不能通过使用其方法self.get_hunger() 访问类字段self.hunger

      您的方法self.get_hunger() 返回值self.hunger(它的副本),但不是变量本身。因此,您可以向该值添加任何数字,但您需要将其写入某处以保持其值。因此,当您运行self.get_hunger()+=hunger 时,您的方法会返回self.hunger 的副本,将hunger 从参数添加到它,然后此副本会丢失,但self.hunger 是相同的。

      所以,如果你想增加self.hunger - 你只需要使用你已经检查过的self.hunger+=hunger

    3. 如果您使用通过引用而不是值传递的变量类型,它实际上会起作用。就像这个例子中的列表一样,但我会说这是一种变态的方式。 ;)

       class Tribute(Person):
           def __init__(self, name, health):
               super().__init__(name, health, -1)
               self.hunger=[0]
      
           def get_hunger(self):
               return self.hunger
      
           def add_hunger(self,hunger):
               self.get_hunger()[0]+=hunger  # dk why can't assign to function call
               if self.get_hunger()[0]>=100:
                   self.go_to_heaven()
      
    4. 对不同的事物使用相同的名称是不好的。它可能会导致一些错误。即使其中一个是变量,另一个是方法。如果您稍后尝试将该方法传递给某个线程 - 它永远不会知道您在那里传递的是哪一个。如果所有名称都不同 - 它更安全且更具可读性。

    【讨论】:

    • 关于替代实现的好建议,如果可能,我总是希望将我的方法的参数命名为与我的属性不同,或者使用引用而不是值。但是,我的作业要求是饥饿属性的初始值为0,没有任何关于列表的建议,因此出现了与副本等有关的所有错误。