views:

104

answers:

5

Suppose I have a class, and I want to reference some elements in the ' __dict__ (for instance, I want to copy the dict and delete the attribute that cannot be pickled), from inside the class.

Problem is, those attributes are "private" so my code ends up looking like so

class MyClasss(object):
    def __init__(self):
          self.__prv=1
    def __getstate__(self):
          ret=self.__dict__.copy()
          del ret['_MyClass__prv']

I reference the class name explicitly in the del statement, which looks a little ugly for me. Is there something nicer? something like MyClass.getPrivateString('prv')

Of course I can implement one myself, but I would be surprised if there isn't a builtin to surpass this problem.

+6  A: 

Consider using only a single underscore for private attributes. These are still considered private but do not get name mangled.

class MyClasss(object):
    def __init__(self):
          self._prv=1
    def __getstate__(self):
          ret=self.__dict__.copy()
          del ret['_prv']
Jon-Eric
thanks, I might use that tip.but I would really like to get an answer for my original question, with the mangling :)
noam
Check my answer, I tried to deal with the name mangling in another way.
jbernadas
@noam: isn't that like asking for an pain-killer so it hurts less while you are hitting yourself with a hammer? @jon-eric: thanks for the pointer to the docs.
msw
A: 

You might try to create a copy of the object, erase its private attributes and then return its __dict__, something like:

class X:
  def __init__(self):
    self.__prv = 0

  def del_privates(self):
    del self.__prv

  def __getstate__(self):
    other = X()
    other.__dict__ = self.__dict__.copy()
    other.del_privates()
    return other.__dict__

After calling __getstate__, the returned dict will not have the __prv member, since it gets erased when other.del_privates() is called.

jbernadas
Or, better yet, just don't deal with name mangling.
Aaron Gallagher
I know it's better, the name mangling is there for some reason. However, I answered because the OP asked how to do it.
jbernadas
A: 

There is no shortcut, but if you just want to avoid naming the class explicitly, you can do something like:

del ret["_%s__%s" % (self.__class__.__name__, "prv")]
thieger
This breaks with subclassing.
Aaron Gallagher
A: 

Better to use your own naming convention for those attributes, such as _prv_x, _prv_y etc. then you can use a loop to selectively remove them

class MyClasss(object):
    def __init__(self):
          self._prv_x=1
          self._prv_y=1
    def __getstate__(self):
          return dict((k,v) for k,v in vars(self).items() if not k.startswith("_prv_"))

In python2.7+

    def __getstate__(self):
          return {k:v for k,v in vars(self).items() if not k.startswith("_prv_")}
gnibbler
Uh, this convention already exists: a single leading underscore.
Aaron Gallagher
@Aaron, the OP needs a way to distinguish variables that are not to be pickled. Zope uses the same idea.
gnibbler
@gnibbler, so why not use the established convention of a single leading underscore?
Aaron Gallagher
@Aaron, perhaps some of the private variables _do_ need to be pickled. This way they still match the convention of having a single leading underscore, but also have a mini-namespace with a special purpose
gnibbler
A: 

At the end, I used a variant of thieger's solution

del ret["_%s__%s" % (MyClasss.__name__, "prv")]

I think this is the most robust way I can write that piece of code, aside from giving up the mangling, which might be the right thing to do, but I was asking what to do in case you actually have mangling :)

noam