1

I'm trying to use Python's @property functionality with below implementation:

class DataForm:

    def __init__(self):

        self._curr_dict = dict()

    @property
    def curr_dict(self):

        return self._curr_dict

    @curr_dict.setter
    def curr_dict(self, key, val):

        if 0 < val < 100:
            self._curr_dict[key] = val

        else:
            raise ValueError("Value is not in range")

getter is working fine, however when I'm trying to set the key/value for curr_dict, it is throwing below error:

ob1 = DataForm()
ob1.curr_dict
ob1.curr_dict = ('Jack', 10)

error:

TypeError                                 Traceback (most recent call last)
<ipython-input-73-cb86bfc26332> in <module>
----> 1 ob1.curr_dict = ('Jack', 10)

TypeError: curr_dict() missing 1 required positional argument: 'val'

I had read somewhere that setter does not take more than one argument. So how do I implement this? Or, this is not a better approach to solve this problem.

My current implemetation is as below and it is working as expected:

class DataForm:

    def __init__(self):
        self.data_dict = dict()

    def create_key_value_pair(self, key, value):
        self.data_dict[key] = value

    def get_dict(self):
        return self.data_dict
Sumit
  • 1,360
  • 3
  • 16
  • 29
  • 1
    The `('jack', 10)` is only one element that is of type tuple – azro Apr 10 '20 at 09:13
  • why aren't you trying to implement something working like `ob1.curr_dict['Jack'] = 10` instead of `ob1.curr_dict = ('Jack', 10)`? – norok2 Apr 10 '20 at 09:22
  • @norok2: Thanks for your suggestion. Currently, i' not sure how to implement this. Do you think i have messed up doing a simple job by using getter/setter? is my "other" approach mentioned in question a better way to do so? – Sumit Apr 10 '20 at 10:09
  • @Sumit Essentially you would need to subclass `dict` and make `curr_dict` an instance of the subclassed dict. The subclassed `dict` shall override `__setitem__` to include the check. I have voted to reopen the question to provide some more details into this direction in an answer. Perhaps you could edit your question accordingly. See also https://stackoverflow.com/questions/2390827/how-to-properly-subclass-dict-and-override-getitem-setitem – norok2 Apr 11 '20 at 06:46

1 Answers1

7

Setters just don't work this way, they're meant to take care of assignment statements to your property, thus they'll only accept one argument, which is whatever is to the right of the = operator. If you want to do further processing on the argument, you need to do it in the setter body:

@curr_dict.setter
def curr_dict(self, arg):
    key, val = arg # <<<<<<<<<<<<<<

    if 0 < val < 100:
        self._curr_dict[key] = val

    else:
        raise ValueError("Value is not in range")

Note: Whether defining a setter to behave this way (in this specific case) is a good idea is questionable. Personally, I think it's a bad idea, because you're turning a dict assignment into a "something = (key, value)" which can possibly raise an error (and to an end-user might not look like a key-value assignment).

GPhilo
  • 18,519
  • 9
  • 63
  • 89
  • Good answer, but I'm not convinced it's questionable. Or maybe I'm just questioning it :-) I see no problem in passing in *any* type that can become a proper value. For example, a complex variable may get a tuple with real and imaginary components, a rational may get a tuple with numerator and denominator, an address may get a list of address lines that you just do `addr = "\n".join(addList) + "\n"` on. With RTTI, endless possibilities abound. Saying it can raise an error is no different than saying an unsigned int field can raise an error if you try to set it to `-1`. – paxdiablo Apr 10 '20 at 09:22
  • @paxdiablo we're delving into opinion-based here, I added a small clarification of why I believe it's a bad idea. I'm not familiar with setter use-cases, so maybe this is not so uncommon, but I'm quite confident there's a better way to handle key-value assignments with constraints – GPhilo Apr 10 '20 at 09:24
  • 2
    GPhilo, key/value assignments, yes I agree, which is why you got a vote from me. I'm just not sure I agree for the *general* case. – paxdiablo Apr 10 '20 at 09:26
  • @paxdiablo I do not think it is questionable *per se*, but it would not be implementing the same syntax that Python developers are used to for similar (identical) objects, and the question itself does not seem to provide a good reason for doing so. – norok2 Apr 10 '20 at 09:26
  • probably "behave this way" should be "behave this way *in this case*" :-) – norok2 Apr 10 '20 at 09:27
  • @paxdiablo I wasn't referring to the general case, that's why I added the clarification :) – GPhilo Apr 10 '20 at 09:28
  • 1
    @GPhilo I mean here: "Note: Whether defining a setter to behave this way is a good idea is questionable." – norok2 Apr 10 '20 at 09:30
  • 1
    @norok2 Oh! Yeah, you're right. Added that ;) – GPhilo Apr 10 '20 at 09:32
  • @GPhilo: Thanks for the explanation. Since I'm not so familiar with getter's/setter's, I had asked for suggestion as well in my question "Or, this is not a better approach to solve this problem?" Can you help me out with a better approach? – Sumit Apr 10 '20 at 10:03
  • 1
    @Sumit I'm also not familiar with designing properties, so I don't think I'm the best source of help here. You might want to post a question on CodeReview or SoftwareEngineering (both are other Stackexchange sites, more on-topic for alternative design suggestions) – GPhilo Apr 10 '20 at 10:06