何时将 getter 和 setter 与 属性 一起使用?

when to use getter and setter with property?

什么时候应该使用 属性 和 getters/setters?不将 属性 与 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

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}")

如果后来发现您确实需要做一些更复杂的事情,您以后可以随时将其变成 属性,无需更改界面。

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

  • 让您完全控制内部数据,例如防止人们进入像 -42 这样的年龄(如果可以,他们 这样做);和
  • 让您可以在不影响客户端的情况下以任何您想要的方式更改基础实现

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

然后您可以完全改变天真的 getter 来自:

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

至:

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

没有任何客户端更改。

《Pythonic》圣战

我个人比较喜欢完全控制的Class。

你的情况:

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)