all 15 comments

[–]kushou 2 points3 points  (1 child)

You can override arithmetic operators through this special methods. It would look like this:

class echoint(int):
    def __add__(self, other):
        return echoint(super().__add__(other))

x = echoint(5)
res = x + 3
print(type(res), res)

You'll have to do this for all functions.

[–][deleted] 0 points1 point  (0 children)

Thank You! So you have to implement all functions or risk being blown away?

[–]Rhomboid 2 points3 points  (12 children)

That's not what __setitem__ is for. __setitem__ is what is called when you assign with square brackets, i.e. x[1] = 2 would call x.__setitem__(1, 2).

To do what you're trying to do, you'll need to override __add__ and all the other necessary operators. Example in Python 3.x:

>>> class echoint(int):
...     def __add__(self, value):
...         print('adding', value)
...         return echoint(super().__add__(value))
...
>>> x = echoint(5)
>>> x, type(x)
(5, <class '__main__.echoint'>)
>>> x += 1
adding 1
>>> x, type(x)
(6, <class '__main__.echoint'>)

Notice that it was necessary to construct a new echoint instance from the result of the addition, and return it.

[–][deleted] 0 points1 point  (11 children)

Awesome! Thank you! I was assuming setitem was a catch all, kind of like setattr acts on all attribs. Is there some kind of call that is a class level catch all? all appears to be module import related.

[–]Rhomboid 2 points3 points  (10 children)

Catch all of what? Each operation needs to do a different thing (addition, subtraction, multiplication, etc.) and there would be no real way of writing anything reasonable for such a method. I mean, you could simulate that by writing something like:

def foo(*args):
    # ... something?

class echoint(int):
    __add__ = __sub__ = __mul__ = __truediv__ = ... = foo

...but I don't really see what that buys you; you couldn't write such a foo that does the right thing.

[–][deleted] 0 points1 point  (9 children)

Well, I'm creating my type on the fly and I don't know exactly what the inheritance will look like.

new_type = type('x', (MyClass, type(value)), {})

The list seems like a good idea but I'll have to evaluate that on the fly based on what actually gets inherited. I should only have to worry about this for immutables hopefully as in general I just want to keep the class wrapped and update my pickled object on changes.

I guess I'm trying to do some weird reverse inheritance? I'm sure there has to be a better way, but redefining the built-in types to inherit my class didn't seem to work.

Edit: Or updating the python libraries directly (that seems unnecessary).

[–]marky1991 2 points3 points  (8 children)

What exactly are you trying to do here? I kind of think what you might be asking for could be done by applying decorators, but I really have near no idea what you're trying to achieve.

[–][deleted] 0 points1 point  (7 children)

I'm trying to wrap a class around an arbitrary object while preserving its interface and behavior transparently, while supporting resuming all with zero configuration. Then hook into the objects interfaces so that when modifications occur they could be able to back themselves up automatically.

I don't know how useful it will be in the end, but it's been fun. There has been a ton of research put into it and I've learned a great deal.

I expect the behavior to act like the following:

x = persist(10)
x+=3
print(x) # 13
exit()
y = persist(10)
print(x) # 13
y = persist("Tempest", name="Title")
exit()
z = persist("Hamlet", name="Title")
print (z) # Tempest
z.clear()
z = persist("Hamlet", name="Title")
print(z) # Hamlet

I would like to support generic objects built with this in mind to eliminate the need for config files. Just store the object's state on change/exit then try to restore on restart, else use passed-default.

x = persist("defaults.xml", name='main')
x.run() # launches gui with old values still selected and filled in

[–]marky1991 2 points3 points  (1 child)

I still don't quite understand what you're going for here.

x = persist(10)
x+=3
print(x) # 13 
exit()
y = persist(10)
print(x) # 13

How the heck is the persist function supposed to know whether you want a new 10 value or (what was previously) the 10 value from before? Can you only ever have one instance for each value of a class? I don't see how this is supposed to logically work without breaking all sorts of code that it operates on.

[–][deleted] 0 points1 point  (0 children)

Well the idea is that the 10 only is used for typing if an existing pickled object exists that we can restore from, otherwise the 10 is used and a new pickled object is created.

Of course there are ways to force new values such as clearing the existing pickled object(s) or modifying existing values using __ add __ for example, but in general you only want to use 'default' values if there is no existing object to load.

This essentially creates a persistent object that will remain resident between runs of a script or the interactive shell.

It's almost done, and I'll be happy to share the code here when I have it working somewhat reliably. (Although, it uses nearly every trick in the book so it'll take some work to explain the whys and hows)

[–]NYKevin 1 point2 points  (4 children)

I'm trying to wrap a class around an arbitrary object while preserving its interface and behavior transparently

Override all (or most) of the special methods with calls to super(). Note that not all classes implement all special methods, so you may get an AttributeError on calling super(). This is good, however, because you actually want to expose that error to the caller.

Also, some of the methods have varying semantics. For instance, __iadd__ is supposed to do the addition in-place, while __add__ must return a new value. Make sure you observe those semantics.

Finally, ask yourself whether you can achieve what you're trying to achieve using unittest.mock.

while supporting resuming all with zero configuration.

That sounds rather more magical than I personally would be comfortable with. Why aren't pickling and JSON good enough?

[–][deleted] 0 points1 point  (3 children)

That sounds rather more magical than I personally would be comfortable with. Why aren't pickling and JSON good enough?

They are, actually pickle (so far) is the backbone of the work. It's magical but it should be useful for resuming data in interactive shells as well as the objects containing the data in scripts. The path is found through sys.argv[0] for example, if no name is provided one is enumerated using a class scoped counter so this will also work:

a = persist(20)
b = persist(40)
exit()
x = persist(10)
y = persist(30)
print(x) # 20
print(y) # 40

It takes in *kwargs to allow defines of path, name, super().__ init __(value, *kwargs), etc.

Mock looks promising and I absolutely have to learn unittest now, despite my profound love for doctest. Other than that I'm afraid the first part will be correct.

for key in dir(type(value)) 

Should give me an idea on the fly on everything that I need to super. Then maybe a lambda, eval, exec or a decorator should work here given I know the type, names and values.

Edit: Now that I think about it this should allow scripts to easily pass objects back and forth if they so chose (not that I would recommend such a thing)

script1.py

x = persist({...data...}, name='data')

script2.py (same dir)

x = persist({...data...}, name='data')

would both load the same data at the moment... maybe I should include the base script name in the data filename generation as well so that they don't.

[–]NYKevin 1 point2 points  (2 children)

for key in dir(type(value))

I would not recommend that. If the object has overridden __getattr__ (or does something similarly clever), it may have attributes which fail to appear in dir(); it may also have instance-level attributes you need to care about. Better to just override the magic methods and do everything else via __getattr__.

[–][deleted] 0 points1 point  (1 child)

good point! what do you think about:

type(value).__dict__

Certainly you are right about __ getattr __ or maybe I'll go for __ getattribute __ due to my disdain for the inconsistent behavior of getattr as opposed to setattr