【问题标题】:when to use getter and setter with property?何时将 getter 和 setter 与属性一起使用?
【发布时间】:2021-10-22 23:21:41
【问题描述】:

什么时候应该使用带有 getter/setter 的属性?不使用带有 getter 和 setter 的属性不是 Pythonic 或错误的吗?我应该还是不应该用属性写它?

例子:

class Person:
    def __init__(self, firstname, lastname, age):
        self.firstname = firstname
        self.lastname = lastname
        self.age = age

    def say_hi(self):
        print(f"""Hi i'm {self.firstname} {self.lastname} and i'm {self.age}""")

    @property
    def age(self):
        return self._age

    @age.setter
    def age(self, newage):
        if not isinstance(newage, int):
            raise TypeError("Expect an Integer")
        self._age = newage

对

class Person2:
    def __init__(self, firstname, lastname, age):
        self.firstname = firstname
        self.lastname = lastname
        self.age = age

    def say_hi(self):
        print(f"""Hi i'm {self.firstname} {self.lastname} and i'm {self.age}""")

    def get_age(self):
        return self.age

    def set_age(self, newage):
        if not isinstance(newage, int):
            raise TypeError("Expect an Integer")
        self.age = newage

【问题讨论】:

  • Python 属性 exist 这样您就不必用get_age 和set_age 做笨拙的Java 事情。不要用 Python 编写 Java。在 Java 中,我们必须使用 getter/setter 保护所有字段,因为没有更好的方法。在 Python 中,字段访问和属性访问看起来完全一样,所以我们得到了统一的访问原则和很好的语法。
  • 这能回答你的问题吗? Using @property versus getters and setters

标签: python python-3.x oop properties


【解决方案1】:

pythonic 的方式是根本不使用 setter 和 getter;只是有一个属性:

class Person:
    def __init__(self, firstname, lastname, age):
        self.firstname = firstname
        self.lastname = lastname
        self.age = age

    def say_hi(self):
        print(f"Hi i'm {self.firstname} {self.lastname} and i'm {self.age}")

如果要检查类型,请使用类型注释和mypy 之类的检查器:

class Person:
    def __init__(self, firstname, lastname, age):
        self.firstname: str = firstname
        self.lastname: str = lastname
        self.age: int = age

    def say_hi(self):
        print(f"Hi i'm {self.firstname} {self.lastname} and i'm {self.age}")

如果后来发现你确实需要做一些更复杂的事情,你可以随时把它变成一个属性,而不用改变接口。

【讨论】:

  • 当某个用户(或者,更有可能是测试人员)执行person.age = -42 时,您将如何处理? :-)
  • 是输入还是代码?从输入中,您将在输入处进行验证以及解析。从代码来看,程序员被认为是团队的一部分;我们都是成年人。
  • sabik,我的观点是每一层都负责检查他们的自己的约束。这就是为什么在数据库中(而不是在用户代码中)强制执行函数、RAII 或关系的前提条件检查。如果它在依赖它的层中,则执行一次。否则,您将在使用该层的每一段代码中实现它。并且“我们都是成年人”掩盖了这样一个事实,即其中一些成年人积极寻找这样的潜在漏洞来绕过安全性:-) 对于一个人来说不太可能,但在更一般的意义上肯定是这样。
【解决方案2】:

您通常应该更喜欢将“受保护”变量(例如以 _ 开头的变量)与属性(不是用户需要调用的单独函数,这很笨拙)一起使用,因为它具有一些优势。这种封装非常方便:

  • 让您完全控制内部数据,例如防止人们输入像-42 这样的年龄(如果可以,他们会这样做);和
  • 允许您以任何您想要的方式更改底层实现,而不会影响客户端。

例如,在最后一点上,您可能希望维护所有名称的单独结构,并简单地将对这些名称的引用存储在您的 Person 类中。这可以让您存储许多更多名称,因为姓氏“Von Grimmelshausen”将被存储一次(在单独的结构中),并且在所有 @ 987654324@ 使用它的对象。

然后你可以完全改变天真的getter:

@property
def surname(self):
    return self._surname

到:

@property
def surname(self):
    return self._surname_db[self._surname_index]

无需对客户端进行任何更改。

【讨论】:

  • 我认为没有抓住重点。属性的要点是您可以将原始字段 (self.surname) 更改为属性,而无需更改客户端代码。从一开始就使用属性,即使是不必要的,也与这种简单性背道而驰,并且破坏了它们对字段进行统一访问的意义。
  • @SilvioMayolo:我的建议是,您应该始终使用 getter 和 setter(某些种类,最好是 Python 中的隐式属性),而不是允许不受限制地访问内部状态,除非有充分的理由允许一个属性成为任何值(并且允许这样做的用例很少)。这不仅意味着变量超出范围,还意味着完全不同类型的变量。例如,假设您的属性之一是列表,并且您允许用户执行 object.should_be_a_list = sys.exit。欢闹几乎肯定会随之而来:-)
  • 这与鸭式打字相反;如果您有一个通常是列表的属性,那么遵循列表协议的任何内容都应该没问题
  • python 甚至没有受保护或私有属性;有一个命名约定,仅此而已
【解决方案3】:

“Pythonic”是一场神圣的斗争。

我个人更喜欢完全控制的班级。

在你的情况下:

class Person:

    def __init__(self, firstname, lastname, age):
        self.firstname = firstname
        self.lastname = lastname
        self.age = age

    def say_hi(self):
        print(f"Hi i'm {self.firstname} {self.lastname} and i'm {self.age}")

    def test_str(self, cosi):
        return self.test(cosi, str)

    @staticmethod
    def test(cosi, neco):
        assert isinstance(cosi, neco), f"Bad value! {cosi} is not instance" \
                                       f" from {neco.__name__}"
        return cosi

    @staticmethod
    def test_positiv_int(num):
        assert 0 < int(num), f"Expect an positiv integer"  # negative value protect
        return int(num)  # if int is like string this returned int

    def __setattr__(self, key, value):
        # important!!!:
        whitedict = dict(firstname=self.test_str,
                         lastname=self.test_str,
                         age=self.test_positiv_int
                         )
        # Call fn from whitedict with parameter
        self.__dict__[key] = whitedict[key](value)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-07-18
    • 2010-11-16
    • 1970-01-01
    相关资源
    最近更新 更多