Python - 类 Property.Setter 不验证字典键上的直接设置

ko_*_*_00 0 python oop dictionary python-descriptors

main():请参阅注释中提到的结果的函数。*一般注意事项:当Class()有一个attribute包含例如str->时,@propery.setter一旦设置就会验证。

但是,当我将 a 存储dictionary在 中时Class.attribute@property.setter如果我直接设置 a ,则 My 不起作用key : value

class CoinJar():

    def __init__(self):
        self._priceDict = {'current' : 0.0, 'high' : 0.0, 'low' : 0.0}
        self.klines = {}
        self.volume = {}

    def __str__(self):
        return f'\n\U0001F36A'

    @property
    def priceDict(self):
        return self._priceDict

    @priceDict.setter
    def priceDict(self, priceDict):

        print('setting price')

        try:
            newPrice = float(priceDict.get('current'))
        except ValueError as e:
            print(f'Input cannot be converted to float {e}')

        # Exceptions
        if len(priceDict.keys()) == 0 or newPrice == None or type(newPrice) not in [int, float]:
            raise ValueError('setting illegal value')

        #setting high, low, current
        self._priceDict['current'] = newPrice

        if newPrice > self._priceDict['high']:
            self._priceDict['high'] = newPrice
        if newPrice < self._priceDict['low'] or self._priceDict['low'] == 0.0:
            self._priceDict['low'] = newPrice


def main():
    btc = CoinJar()
    btc.priceDict = {'current' : 500}           # This is calling priceDict.setter => accepts and performs the .setter expressions
    btc.priceDict['flamingo'] = 20              # This is not calling priceDict.setter => can do what i want, with 'current, high, flamingo, *'
    btc.priceDict = {'flamingo' : 500}          # This is calling priceDict.setter => raises Exception (as expected)
    print(btc)

def retrieveData():
    ...

def analyseData():
    ...

if __name__ == '__main__':
    main()
Run Code Online (Sandbox Code Playgroud)

Gri*_*mar 6

看看这个简化的例子:

class AClass:
    def __init__(self):
        self._some_dict = None

    @property
    def some_dict(self):
        print('getting')
        return self._some_dict

    @some_dict.setter
    def some_dict(self, value):
        print('setting')
        self._some_dict = value


an_obj = AClass()
an_obj.some_dict = {}  # setter gets called
an_obj.some_dict['a_key'] = 1  # getter gets called, as dict is being accessed
Run Code Online (Sandbox Code Playgroud)

当需要设置属性本身的值时,将调用属性的属性设置器。即当您为属性分配新字典时dict

当访问属性(“读取”/“获取”)时,将调用属性 getter。

当您以其他方式操作属性(例如为字典设置键或值)时,不会调用 setter。你可以触发它,但你必须覆盖字典。

像这样的东西:

class MyDict(dict):
    def __setitem__(self, key, *args, **kwargs):
        print('setting a dictionary value')
        super().__setitem__(key, *args, **kwargs)


class AClass:
    def __init__(self):
        self._some_dict = None

    @property
    def some_dict(self):
        print('getting')
        return self._some_dict

    @some_dict.setter
    def some_dict(self, value):
        print('setting')
        self._some_dict = MyDict(value)


an_obj = AClass()
an_obj.some_dict = {}  # setter gets called
an_obj.some_dict['a_key'] = 1  # getter gets called, as well as item setter

# Note: this just calls setter, as it just directly sets the attribute to the new dict:
an_obj.some_dict = {'a_key': 1}
Run Code Online (Sandbox Code Playgroud)

另一件需要注意的事情是,上面的代码不会自动递归地工作。也就是说,如果您的字典包含更多字典,它们不会自动转换为MyDict,因此会发生这种情况:

an_obj = AClass()
an_obj.some_dict = {}  # setter
an_obj.some_dict['a_key'] = {}  # getter and item setter
an_obj.some_dict['a_key']['another_key'] = 1  # only getter
Run Code Online (Sandbox Code Playgroud)

MyDict您可以通过将任何dict值转换为 a 来继续添加功能MyDict,但还有其他问题需要考虑 - 根据需要进行调整:

class MyDict(dict):
    def __setitem__(self, key, value, *args, **kwargs):
        print('setting a dictionary value')
        if isinstance(value, dict) and not isinstance(value, MyDict):
            value = MyDict(value)
        super().__setitem__(key, value, *args, **kwargs)
Run Code Online (Sandbox Code Playgroud)