1

For the sake of discussion, lets say I want to write and read values from a dictionary but I want to append some string to the dictionary key. Instead of having the user do the string manipulation, I want to hide it inside setter and getter methods. This works no problem with the Python setter method since I can send a list full of information that can be parsed. It does NOT, however, work with the getter method since arguments cannot be sent to getter. I have concocted the workaround shown below where the getter returns a function which accepts the arguments and does what I want. I'm just wondering if this acceptable Python or if I will regret using this approach.

class tester(object):
    def __init__(self):
        self._dict = {}

    def _set(self,some_list):
        self._dict.update({some_list[0]+'_'+some_list[1]: some_list[2]})

    def _get(self):
        return self._func

    def _func(self,key1,key2):
        keys = key1+'_'+key2
        return self._dict[keys]

    props = property(_get, _set)

a = tester()
value = [1,2,3,4,5]
a.props = ('main_key','sub_key',value)
print(a.props('main_key','sub_key'))

I have searched high and low for ways to use setter and getter that suit my needs, but all I see are people asserting that Python doesn't need them and we should all just be adults and access the attributes directly. The closest I've found is this question, but I find my solution more direct.

Community
  • 1
  • 1
2cynykyl
  • 983
  • 9
  • 12
  • i don't think there is anything per se wrong with this. And since it is nicely encapsulated i don't really see you having any issues with it. Imho its kinda weird to do this though =) – Enermis Dec 23 '13 at 15:08
  • 1
    You could also use `__getitem__` and `__setitem__` – smeso Dec 23 '13 at 15:13
  • @Faust: I hate to be too demanding, but could elaborate on your suggestion, or point me to something sort of example? – 2cynykyl Dec 23 '13 at 16:22
  • @2cynykyl I have answered giving you an example. – smeso Dec 23 '13 at 16:42

2 Answers2

4

properties (or any other descriptor) are for computed attributes, that is members that have the semantic of an attribute but needs some computation either on set and/or get. By 'semantic of an attribute', I mean it makes sense, from the client's code, to write:

something = obj.attribute

and / or

obj.attribute = something_else

As soon as you have to pass arguments to a getter or more than one argument to the setter, it doesn't have an attribute semantic, obviously, so you cannot use a computed attribute. The solution is simple: use an explicit getter/setter pair.

The point about getters/setters not being needed in Python is that for anything that has attribute's semantic (but not anything else) you can always turn a direct attribute access to a computed one if and when you need it without breaking the client code. That's not your case here obviously.

tl;dr: abusing properties the way you do is not pythonic and you'd be better with explicit getter and setter.

bruno desthuilliers
  • 75,974
  • 6
  • 88
  • 118
  • You could view "more than one argument to a setter" as just passing a single `tuple`-valued argument... – ely Dec 23 '13 at 15:24
  • @EMS : and how do you handle getters needing arguments exactly ?-) – bruno desthuilliers Dec 23 '13 at 15:58
  • @brunodesthuilliers: I currently do use explicit getter and setter, but I was drawn to the clean/consistent syntax of using `property`. I really appreciate your description of direct vs computed attributes. If I understand you correctly, then sending arguments to the 'setter' is cheating, which clarifies my confusion about why `getter` was much less flexible than `setter` – 2cynykyl Dec 23 '13 at 16:16
  • @2cynykyl: the syntax for properties is clear and consistent exactly because it **does one and only thing**. The examples of your code (the way you wanted it to look) were very confusing. So, yes, I upvoted both answers: you should use either custom methods or `__setitem__`. – ilya n. Dec 23 '13 at 16:48
  • Well, a setter (a property or other descriptor's setter that is) do expect one argument of course - the value you want to set your attribute to. A _getter_ should not expect any argument obviously. Also set and get are supposed to be as symetrical as possible - one doesn't expect that after settings an attribute's value to a list or tuple, getting the same attribute returns a callable taking one less argument ;) More seriously: this is legal Python but it's also as unpythonic as possible. Your API should be along the line of `set_value((key1, key2), value) and `get_value((key1, key2))`. – bruno desthuilliers Dec 23 '13 at 16:48
  • @EMS : I was talking about about a descriptor's getter of course - but I admit it wasn't necessarily quite clear from the context ;) – bruno desthuilliers Dec 23 '13 at 18:38
1

Here is an example of what you could do using __getitem__ and __setitem__:

class tester(object):

    class Subelement(object):
        def __init__(self, obj, key):
            self.obj = obj
            self.key = key

        def __getitem__(self, subkey):
            return self.obj._dict[self.key + '_' + subkey]

        def __setitem__(self, subkey, value):
            self.obj._dict[self.key + '_' + subkey] = value

    def __init__(self):
        self._dict = {}

    def __getitem__(self, key):
        return Subelement(self, key)
smeso
  • 4,165
  • 18
  • 27