繁体   English   中英

什么时候对属性使用 getter 和 setter?

[英]when to use getter and setter with property?

什么时候应该使用带有 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

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)

您的代码的第二个版本(指的是 class2)使用两个实例方法,即get_ageset_age ,它们没有多大用处,因为您可以在不调用get_age方法的情况下检索实例的 age 属性,您也可以设置 age 属性在不调用set_age方法的情况下进行任何操作。 此外,如果您希望用户使用给定的实例方法检索或设置 age 属性,则之前使用您的 class 的用户将不得不更改他们的代码,这是我们不希望的。


现在,您的代码的第一个版本(指的是 class1)非常有用,因为您可以使用属性装饰器对 age 属性施加限制。 您可以将 age 属性设置为只读或读写,并且您可以正常检索或设置实例的 age 属性,而无需调用任何方法。

此外,由于您明确需要在代码的第二个版本中的实例上调用set_age方法,因此对于这段逻辑:

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

执行以便用户不能将任何任意值放入 age 属性,另一方面,当您在使用属性时尝试设置 age 属性时,它会隐式发生。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM