[英]How do getters and setters work in Python?
以下是CS50P的一段代碼。
我不明白它是如何工作的,似乎找不到合適的解釋。
首先,一旦使用用戶輸入的 arguments 名稱和家庭調用 Student 構造函數,隨后將使用相同的 arguments 調用init方法。現在,我不清楚以下兩行中到底發生了什么:
self.name = name
self.house = house
本質上,據我了解,由於 self.name 中的“name”與 @name.setter 下的 name(self, name) 匹配,self.name = name 以 name 的值作為參數調用 name(self, name)。 沒有給出返回值,而是創建了一個新的實例變量,即 _name,它被分配給與 name 相同的值(如果錯誤檢查通過)。 我不明白為什么有必要用開頭的下划線代替名稱來創建這個新變量。 另外,我想對“self.name”的真正作用進行更“深入”的解釋,因為我認為我的理解非常有限,甚至可能是不正確的。
此外,我不知道從哪里冒出來的 getter 和 setter 的想法,除了被解釋說它們允許在設置屬性值時驗證用戶數據,無論它們是在init function 中還是在 class 之外設置共。 但是,它們在“幕后”的真正含義是什么?setter 引用實例變量而不是 getter 的意義是什么? “property”和“name.setter”這兩個名字從何而來,開頭的“@”呢? 這是我第一次看到這種語法,所以它對我來說非常混亂和不合邏輯。
class Student:
def __init__(self, name, house):
self.name = name
self.house = house
def __str__(self):
return f"{self.name} from {self.house}"
@property
def name(self):
return self._name
@name.setter
def name(self, name):
if not name:
raise ValueError("Invalid name")
self._name = name
@property
def house(self):
return self._house
@house.setter
def house(self, house):
if house not in ["Gryffindor", "Hufflepuff", "Ravenclaw", "Slytherin"]:
raise ValueError("Invalid house")
self._house = house
def main():
student = get_student()
print(student)
def get_student():
name = input("Name: ")
house = input("House: ")
return Student(name, house)
if __name__ == "__main__":
main()
我認為,提議的副本提供與此答案大致相同的信息。 但是,正如您所說的那樣,您很難理解它,您的部分困惑可能源於property
類型如何利用裝飾器語法,因此我將嘗試從根本不使用裝飾器的等效定義開始。 (為簡潔起見,我將完全省略house
屬性,只關注name
屬性,並跳過__init__
和__str__
,它們不會改變原始定義。)
class Student:
...
def _name_getter(self):
return self._name
def _name_setter(self, name):
if not name:
raise ValueError("Invalid name")
self._name = name
name = property(_name_getter, _name_setter)
del _name_getter, _name_setter
由於描述符協議的工作方式,通過實例訪問class屬性name
會調用property
的__get__
方法,該方法會調用 getter。 同樣,通過實例對 class 屬性的賦值會調用property
的__set__
方法,該方法調用 setter。 (鏈接的 HOWTO 還提供了property
class 的純 Python 定義,因此您可以在此處准確了解描述符協議的應用方式。)
簡單來說,
s.name
變為Student.__dict__['name'].__get__(s, Student)
,它調用_name_getter_(s)
。s.name = "Bob"
變成Student.__dict__['name'].__set__(s, "Bob")
,調用_name_setter(s, "Bob")
。 並不是說_name_getter
和_name_setter
,雖然像實例方法一樣定義,但實際上從未用作實例方法。 這就是為什么我在創建 class 之前從 class 命名空間中刪除這兩個名稱。 它們只是property
為我們調用的兩個常規函數。
現在,我們可以利用property
定義的一些輔助方法來轉換回原來的基於裝飾器的定義。 property.setter
是一個實例方法,它將 setter function 作為參數,並返回一個新實例,該實例使用原始屬性的所有功能,但用新實例替換任何現有的 setter。 考慮到這一點,我們可以將定義更改為
class Student:
...
def _name_getter(self):
return self._name
def _name_setter(self, name):
if not name:
raise ValueError("Invalid name")
self._name = name
# Define a read-only property, one with only a getter
name = property(_name_getter)
# Replace that with a new property that also has a setter
name = name.setter(_name_setter)
del _name_getter, _name_setter
這兩個屬性都分配給相同的屬性name
。 如果我們為第二個賦值使用不同的名稱,我們的 class 將有兩個不同的屬性操作self._name
:一個只有一個 getter,一個有一個 getter 和一個 setter。
property
僅應用於一個參數,因此我們可以回到使用裝飾器語法。 我們不是首先定義_name_getter
,然后對其應用property
,而是先命名 getter name
並用property
修飾它。 (因此名稱name
將立即將其原始 function 值替換為包裝原始 function 的property
值。)
class Student:
...
@property
def name(self):
return self._name
def _name_setter(self, name):
if not name:
raise ValueError("Invalid name")
self._name = name
name = name.setter(_name_setter)
del _name_setter
同樣,我們可以用也命名為name
的 setter 的修飾替換對預定義的_name_setter
function 的name.setter
的顯式調用。 (由於裝飾器的實際實現方式,新的 function name
將在評估name.setter
之后定義以獲取裝飾器本身,但在調用裝飾器之前,因此我們在最終分配之前使用舊屬性定義新屬性新property
命名為name
。)
class Student:
...
@property
def name(self):
return self._name
@name.setter
def name(self, name):
if not name:
raise ValueError("Invalid name")
self._name = name
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.