簡體   English   中英

是否可以創建一個在修改時實現其所有屬性的對象?

[英]Is it possible to create an object that actualize all its attributes when modified?

在這個例子中,應該怎么做才能使print(left_hand.number_of_fingers)返回4而不是5

class Hand:
    def __init__(self, fingers:list):
        self.fingers = fingers
        self.number_of_fingers = len(fingers)

left_hand = Hand(["thumb", "index", "middle", "ring", "pinkie"])
left_hand.fingers.pop()
print(left_hand.number_of_fingers) # I want this to actualize and be 4, not 5

我找到了一個使用@property的解決方案

class Hand:
    def __init__(self, fingers:list):
        self.fingers = fingers

    @property
    def number_of_fingers(self):
        return len(self.fingers)

但我並不滿意,因為如果計算number_of_fingers很昂貴,我們只想在修改fingers時計算它,而不是每次用戶要求屬性number_of_fingers時計算它。

因此,在第一個代碼中(不使用@property ),您將獲得 5 而不是 4 的輸出,因為您只是在初始化Hand對象時將len(fingers)的值分配給number_of_fingers屬性,而number_of_fingers屬性不是越來越多的fingers

所以即使在代碼之間修改了left_hand.fingers ,也不會影響number_of_fingers的值。 人們無法改變這種行為。

另外你不需要那個@property ,我測試了發現不寫也不會出錯。

終於來到

但我並不滿意,因為如果計算number_of_fingers很昂貴,我們只想在修改fingers時計算它,而不是每次用戶要求屬性number_of_fingers時計算它。

哪里需要這么大的計算能力?

問題是Hand類中的底層列表(即self.fingers )沒有被充分封裝,因此任何用戶都可以修改它,例如通過調用left_hand.fingers.pop()甚至left_hand.fingers.pop()分配一個新列表. 因此,您不能假設它在調用number_of_fingers之間沒有被修改,因此您別無選擇,只能在該調用中計算其長度。

解決方案是控制您班級的客戶可以做什么和不可以做什么。 最簡單的方法是使用name mangling 也就是說,您用兩個前導下划線字符作為屬性名稱的前綴。 這使得您的類的客戶端很難(盡管並非不可能)從類外部訪問這些屬性(我們假設您的用戶不是故意惡意的)。 因此我們現在必須提供一個pop方法:

class Hand:
    def __init__(self, fingers:list):
        self.__fingers = fingers
        self.__number_of_fingers = len(fingers)

    def pop(self):
        assert(self.__fingers)
        self.__number_of_fingers -= 1
        return self.__fingers.pop()

    @property
    def number_of_fingers(self):
        return self.__number_of_fingers

left_hand = Hand(["thumb", "index", "middle", "ring", "pinkie"])
print(left_hand.pop())
print(left_hand.number_of_fingers)

印刷:

pinkie
4

並不是建議您實際執行以下操作,但是如果您願意,可以通過創建特殊的類裝飾器@Private@Public來獲得更詳細的@Private ,它們會將您的類包裝在一個新類中並檢查對您的屬性的訪問以確保您是不訪問定義為私有的那些屬性。 您可以使用@Private裝飾器來定義那些私有的屬性/方法(其他一切都被認為是公共的),或者使用@Public裝飾器來定義那些公共的屬性/方法(其他一切都被認為是私有的),但不能同時使用兩者。 您通常會使用前導的單個下划線命名您的私有屬性,這是告訴用戶該屬性/方法將被視為私有的約定。

這更多地是為了捕捉無意中訪問本應是私有的屬性。 如果使用-O Python 標志執行代碼,則不會進行運行時檢查。

def accessControl(failIf):
    def onDecorator(aClass):
        if not __debug__:
            return aClass
        else:
            class onInstance:
                def __init__(self, *args, **kargs):
                    self.__wrapped = aClass(*args, **kargs)

                def __getattr__(self, attr):
                    if failIf(attr):
                        raise TypeError('private attribute fetch: ' + attr)
                    else:
                        return getattr(self.__wrapped, attr)

                def __setattr__(self, attr, value):
                    if attr == '_onInstance__wrapped':
                        self.__dict__[attr] = value
                    elif failIf(attr):
                        raise TypeError('private attribute change: ' + attr)
                    else:
                        setattr(self.__wrapped, attr, value)
            return onInstance
    return onDecorator

def Private(*attributes):
    return accessControl(failIf=(lambda attr: attr in attributes))

def Public(*attributes):
    return accessControl(failIf=(lambda attr: attr not in attributes))

@Private('_fingers', '_number_of_fingers')
class Hand:

    def __init__(self, fingers:list):
        self._fingers = fingers
        self._number_of_fingers = len(fingers)

    def pop(self):
        assert(self._fingers)
        self._number_of_fingers -= 1
        return self._fingers.pop()

    @property
    def number_of_fingers(self):
        return self._number_of_fingers


left_hand = Hand(["thumb", "index", "middle", "ring", "pinkie"])
print(left_hand.pop())
print(left_hand.number_of_fingers)
# Thsis will throw an exception:
print(left_hand._fingers)

印刷:

pinkie
4
Traceback (most recent call last):
  File "C:\Booboo\test\test.py", line 50, in <module>
    print(left_hand._fingers)
  File "C:\Booboo\test\test.py", line 9, in __getattr__
    raise TypeError('private attribute fetch: ' + attr)
TypeError: private attribute fetch: _fingers

印刷:

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

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