【问题标题】:How do getters and setters work in Python?getter 和 setter 在 Python 中是如何工作的?
【发布时间】:2022-11-05 11:22:45
【问题描述】:

我正在学习 getter 和 setter ,我的理解是它们的使用是为了没有人可以直接更改对象的属性。在示例中

class Person:
    def __init__(self, name, age):
        self._name = name
        self._age = age

    def get_age(self):
        return self._age

    def set_age(self, new_age):
        if isinstance(new_age, int) & new_age>0 & new_age<120:
            self._age = new_age

    def get_name(self):
        return self._name
    
    def __str__(self):
        return 'Person[' + self._name + '] is ' + str(self._age)
    
p1 = Person("Sandeep", 49)

我创建了一个对象p1,将年龄设置为49。因为我已经创建了set_age 函数,所以我希望我们只能通过set_age 更改p1 的年龄,而不是通过常规方式。但这并没有发生,我也可以通过 p1._age = 35 来更改 p1 的年龄。那么,如果我仍然能够直接访问属性,那么制作 set_age 函数有什么好处?

我想,我错过了一些东西,请帮忙。

【问题讨论】:

  • 这回答了你的问题了吗? What's the pythonic way to use getters and setters?
  • 以下划线(_)开头的变量被认为是私有变量,理想情况下不应修改。
  • 如果使用双下划线self.__age,实际上可以防止从外部直接访问该属性,但是一旦开始使用继承就不建议这样做。
  • @LeopardShark 我想,不。

标签: python getter-setter


【解决方案1】:

您需要告诉 python 如何将 getter 和 setter 与实际变量名相关联。为此,您可以使用内置的 property 函数,如下所示:

class Person
    def __init__(self, name, age):
        self._name = name
        self._age = age

    def get_age(self):
        return self._age

    def set_age(self, new_age):
        if isinstance(new_age, int) & new_age>0 & new_age<120:
            self._age = new_age

    def get_name(self):
        return self._name
    name = property(get_name)
    age = property(get_age, set_age)

    def __str__(self):
        return 'Person[' + self.name + '] is ' + str(self.age)
    
p1 = Person("Sandeep", 49)

然后,不要引用_name_age,而是使用nameage

【讨论】:

    【解决方案2】:

    使用 getter 和 setter 的原因是,如果您想做一些比使用 foo.bar 设置和属性更复杂的事情。在您的情况下,set_age 有一个

    isinstance(new_age, int) & new_age>0 & new_age<120
    

    检查,这不可能与原始属性有关。 (旁注:你应该使用and 而不是&amp;。)

    是的,有人仍然可以做p1._age = -1,然后他们的代码将无法工作,但他们为什么会这样做呢?它只是使他们的代码不起作用。

    您的get_name 功能不如age 有用。它基本上使name 只读,这可能有用也可能没用。

    在 Python 中创建 setter 和 getter 时,通常使用 @property 装饰器。这意味着函数可以像属性一样被调用,因此您可以使用p1.name 而不是p1.get_name()。同样,p1.set_age(3) 变为 p1.age = 3

    您可能想在__init__ 中使用年龄设置器,因为Person 的年龄在创建时会被验证。

    这是进行这些更改的版本(以及其他一些可读性改进)。

    class Person:
        def __init__(self, name, age):
            self._name = name
            self.age = age
    
        @property
        def age(self):
            return self._age
    
        @age.setter
        def age(self, new_age):
            if isinstance(new_age, int) and 0 < new_age < 120:
                self._age = new_age
        
        @property
        def name(self):
            return self._name
    
        def __str__(self):
            return f"Person[{self.name}] is {self.age}"
        
    p1 = Person("Sandeep", 49)
    

    【讨论】:

      【解决方案3】:

      我的理解是使用它们是为了没有人可以直接更改对象的属性。

      你的理解是错误的。没有神奇的规则可以告诉 python 解释器oh, this class has a setter, so direct access to the fields aren't allowed any more

      换句话说:这仅仅是“按惯例工作”。当你一个具有 setter 方法的 python 类,那么你知道不应该直接访问这些字段,尽管您仍然可以这样做。

      请注意:python 在这些事情上大多相当宽容。很多事情只是“按照惯例”。这只是 python 的“本质”:您可以做很多其他语言,尤其是像 Java 这样的静态类型语言不允许做的事情。

      【讨论】:

      • 如果只是约定俗成的问题,我们为什么要编写全新的函数?我们可以简单地通过在属性前添加下划线来实现这个目标,据我所知,我们这样做了。
      【解决方案4】:

      现在我意识到我问了一个愚蠢的问题。使用setters 的原因很明显,创建用于设置值的函数是对其应用约束的一种非常方便的方法。在上面的例子中,约束是人的年龄应该是正数并且小于 120。没有setters 就不可能实现这样的约束。

      【讨论】:

        【解决方案5】:

        一般来说你不要除非您现在特别需要它们,否则在 python 中编写 getter 和 setter 代码。 (但如果你以后需要它们怎么办?我稍后会解释。)

        大多数时候你应该只定义实例变量,然后在需要使用这些变量的代码中,直接访问它们,即:

        p1 = Person("Sandeep",49)
        print("%s is %d years old." % (p1._name, p1._age))
        # prints "Sandeep is 49 years old."
        p1._age = 50
        print("Now %s is %d years old." % (p1._name, p1._age))
        # prints "Now Sandeep is 50 years old."
        

        稍后,当您真正需要它们时,您才可以添加 getter/setter 方法。

        当您稍后添加 getter/setter 方法时,您不要需要更改任何直接访问实例变量的现有代码。相反,请使用 @property 装饰器,如上面 LeopardShark 的回答 (https://stackoverflow.com/a/71080050/1813403) 所示。

        @property 装饰器会生成一些神奇的代码,当现有代码尝试直接访问实例变量时,它会拦截新的 getter/setter 方法,并改为通过 getter/setter 方法发送请求。因此,现有代码将继续工作,但会调用您的新 getter/setter 方法,而不是直接使用属性。

        【讨论】:

          猜你喜欢
          • 2011-01-03
          • 2014-03-23
          • 2022-07-21
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2020-02-09
          相关资源
          最近更新 更多