tags:

views:

159

answers:

3

Hi Pythonistas,

I'd like to serialize Python objects to and from the plist format (this can be done with plistlib). My idea was to write a class PlistObject which wraps other objects:

def __init__(self, anObject):
     self.theObject = anObject

and provides a "write" method:

def write(self, pathOrFile):
    plistlib.writeToPlist(self.theObject.__dict__, pathOrFile)

Now it would be nice if the PlistObject behaved just like wrapped object itself, meaning that all attributes and methods are somehow "forwarded" to the wrapped object. I realize that the methods __getattr__ and __setattr__ can be used for complex attribute operations:

    def __getattr__(self, name):
         return self.theObject.__getattr__(name)

But then of course I run into the problem that the constructor now produces an infinite recursion, since also self.theObject = anObject tries to access the wrapped object.

How can I avoid this? If the whole idea seems like a bad one, tell me too.

+2  A: 

But then of course I run into the problem that the constructor now produces an infinite recursion, since also self.theObject = anObject tries to access the wrapped object.

That's why the manual suggests that you do this for all "real" attribute accesses.

theobj = object.__getattribute__(self, "theObject")
Unknown
+2  A: 

Unless I'm missing something, this will work just fine:

def __getattr__(self, name):
    return getattr(self.theObject, name)


Edit: for those thinking that the lookup of self.theObject will result in an infinite recursive call to __getattr__, let me show you:

>>> class Test:
...     a = "a"
...     def __init__(self):
...         self.b = "b"
...     def __getattr__(self, name):
...         return 'Custom: %s' % name
... 
>>> Test.a
'a'
>>> Test().a
'a'
>>> Test().b
'b'
>>> Test().c
'Custom: c'

__getattr__ is only called as a last resort. Since theObject can be found in __dict__, no issues arise.

Stephan202
It will not. Inside __getattr__, it needs to find self.theObject. so it calls __getattr__, leading to an infinite loop.
dF
Nope, it does not need __getattr__, because theObject is in __dict__. I updated the answer with an example.
Stephan202
+1  A: 

I'm glad to see others have been able to help you with the recursive call to __getattr__. Since you've asked for comments on the general approach of serializing to plist, I just wanted to chime in with a few thoughts.

Python's plist implementation handles basic types only, and provides no extension mechanism for you to instruct it on serializing/deserializing complex types. If you define a custom class, for example, writePlist won't be able to help, as you've discovered since you're passing the instance's __dict__ for serialization.

This has a couple implications:

  1. You won't be able to use this to serialize any objects that contain other objects of non-basic type without converting them to a __dict__, and so-on recursively for the entire network graph.

  2. If you roll your own network graph walker to serialize all non-basic objects that can be reached, you'll have to worry about circles in the graph where one object has another in a property, which in turn holds a reference back to the first, etc etc.

Given then, you may wish to look at pickle instead as it can handle all of these and more. If you need the plist format for other reasons, and you're sure you can stick to "simple" object dicts, then you may wish to just use a simple function... trying to have the PlistObject mock every possible function in the contained object is an onion with potentially many layers as you need to handle all the possibilities of the wrapped instance.

Something as simple as this may be more pythonic, and keep the usability of the wrapped object simpler by not wrapping it in the first place:

def to_plist(obj, f_handle):
    writePlist(obj.__dict__, f_handle)

I know that doesn't seem very sexy, but it is a lot more maintainable in my opinion than a wrapper given the severe limits of the plist format, and certainly better than artificially forcing all objects in your application to inherit from a common base class when there's nothing in your business domain that actually indicates those disparate objects are related.

Jarret Hardie
Thanks for the extensive comment. Pickle is not an option since i need the Plist format. I'm aware that the wrapper is not really generic and can only handle simple types. At the moment it works fine, but if I run into problems I'll probably go for the simple function like you suggested.
D-Bug