0

Is there a way to make a dictionary of functions that use set and get statements and then use them as set and get functions?

class thing(object):
    def __init__(self, thingy)
        self.thingy = thingy
    def __get__(self,instance,owner):
        return thingy
    def __set__(self,instance,value):
        thingy += value

theDict = {"bob":thing(5), "suzy":thing(2)}

theDict["bob"] = 10

wanted result is that 10 goes into the set function and adds to the existing 5

print theDict["bob"]
>>> 15

actual result is that the dictionary replaces the entry with the numeric value

print theDict["bob"]
>>> 10

Why can't I just make a function like.. theDict["bob"].add(10) is because it's building off an existing and already really well working function that uses the set and get. The case I'm working with is an edge case and wouldn't make sense to reprogram everything to make work for this one case.

I need some means to store instances of this set/get thingy that is accessible but doesn't create some layer of depth that might break existing references.

Please don't ask for actual code. It'd take pages of code to encapsulate the problem.

martineau
  • 99,260
  • 22
  • 139
  • 249
  • What the heck? No, `__get__` and `__set__` don't work anything like that. I would be highly suspicious of your existing design, if your existing design is storing `thingy` on the descriptor itself instead of objects of the class the descriptor is attached to. – user2357112 supports Monica Jul 25 '16 at 16:54
  • I'm pretty confused about this question too... I don't understand how `__get__` and `__set__` are related to what you're trying to do. I suppose you _could_ create a wrapper class that overrides `__getitem__` and `__setitem__` to update the delegated dict in whatever way you want. However, without seeing some real code (or at least a minimal example of what you're really trying to accomplish), it'll be hard to give more advice than that ... – mgilson Jul 25 '16 at 16:58
  • The main problem you having it that you have to decide which object is responsible for your custom behavior. Right now you are performing action on dictionary (`theDict["bob"] = 10`) assign 10 to key `bob` in `theDict` (as you can see, this description does not even mention value previously stored under said key), but expect to exploit custom logic in `thing`. If custom logic is in `thing`, ask `thing` to do it, do not ask dict. And if you can't, rethink your design. – Łukasz Rogalski Jul 25 '16 at 17:02
  • This is all happening through maya, which adds a layer of programming weirdness that isn't the easiest to navigate around. The program's complexity comes back to Maya not including any native way of tracking objects besides by name. To address this shortfall I had to create a class based off of an OpenMaya plugin that tracks dags and objs. This creates a nightmare of initialization orders, where I have to be very deliberate about when things are initialized. Despite all this bullpucky, I managed to make the program work for most cases except now I'm expanding it into shader nodes, which add.. – Colin Knueppel Jul 25 '16 at 18:13
  • .. list inputs, meaning expandable. My problem is that I need a means to add these touchy classes to the final shader class in a list or dictionary fashion, but the original functions use __set__ and __get__. If I were able to use a dictionary I could do something like rotate3D[0]["x"] = connectA, rotate3D[1]["x"] = connectB in an addition node, and be able to get the sum of the X's. However, whether dicts or lists, if I assign to them they are replaced, and the existing functions don't work. So the question is how can I access a set and get in a way that allows for variable numbers of inputs. – Colin Knueppel Jul 25 '16 at 18:21

3 Answers3

1

No, because to execute theDict["bob"] = 10, the Python runtime doesn't call any methods at all of the previous value of theDict["bob"]. It's not like when myObject.mydescriptor = 10 calls the descriptor setter.

Well, maybe it calls __del__ on the previous value if the refcount hits zero, but let's not go there!

If you want to do something like this then you need to change the way the dictionary works, not the contents. For example you could subclass dict (with the usual warnings that you're Evil, Bad and Wrong to write a non-Liskov-substituting derived class). Or you could from scratch implement an instance of collections.MutableMapping. But I don't think there's any way to hijack the normal operation of dict using a special value stored in it.

Steve Jessop
  • 257,525
  • 32
  • 431
  • 672
  • +1 on the `collections.MutableMapping` suggestion. See: [http://stackoverflow.com/a/3387975/6084928](http://stackoverflow.com/a/3387975/6084928) – Lex Scarisbrick Jul 25 '16 at 17:35
  • @Lex: I think implementing a concrete `collections.MutableMapping` container is overkill—a relatively simple subclass of `dict` does nicely. See [my answer](http://stackoverflow.com/a/38574478/355230). – martineau Jul 25 '16 at 18:38
1

You could do it if you can (also) use a specialized version of the dictionary which is aware of your Thing class and handles it separately:

class Thing(object):
    def __init__(self, thingy):
        self._thingy = thingy
    def _get_thingy(self):
        return self._thingy
    def _set_thingy(self, value):
        self._thingy += value

    thingy = property(_get_thingy, _set_thingy, None, "I'm a 'thingy' property.")

class ThingDict(dict):
    def __getitem__(self, key):
        if key in self and isinstance(dict.__getitem__(self, key), Thing):
            return dict.__getitem__(self, key).thingy
        else:
            return dict.__getitem__(self, key)

    def __setitem__(self, key, value):
        if key in self and isinstance(dict.__getitem__(self, key), Thing):
            dict.__getitem__(self, key).thingy = value
        else:
            dict.__setitem__(self, key, value)


theDict = ThingDict({"bob": Thing(5), "suzy": Thing(2), "don": 42})

print(theDict["bob"])  # --> 5
theDict["bob"] = 10
print(theDict["bob"])  # --> 15

# non-Thing value
print(theDict["don"])  # --> 42
theDict["don"] = 10
print(theDict["don"])  # --> 10
martineau
  • 99,260
  • 22
  • 139
  • 249
  • This almost works for me, but with one exception. The number of passed arguments. My class that I'm putting in the dictionary uses the instance that's passed to it. This dict method doesn't appear to pass that instance. Is there a way to do that in this method? – Colin Knueppel Jul 26 '16 at 20:20
0

theDict["bob"] = 10 is just assign 10 to the key bob for theDict. I think you should know about the magic methods __get__ and __set__ first. Go to: https://docs.python.org/2.7/howto/descriptor.html Using a class might be easier than dict.

Joey.Chang
  • 144
  • 2
  • 14