簡體   English   中英

Python 中的 getter 和 setter 是如何工作的?

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

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM