[英]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_age
和set_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.