[Tutor] overriding instance attributes with keywords

eryksun eryksun at gmail.com
Tue Aug 14 12:01:52 CEST 2012


On Mon, Aug 13, 2012 at 10:28 PM, Steven D'Aprano <steve at pearwood.info> wrote:
>
>> def new_with_overrides(obj1, **kwds):
>>      obj2 = obj1.__new__(obj1.__class__)
>>      obj2.__dict__.update(obj1.__dict__)
>>      for k, v in kwds.items():
>>          if k in obj2.__dict__:
>>              obj2.__dict__[k] = v
>>      return obj2
>
> In general, this can fail too. The instance may have __slots__, it
> may not have a __dict__ at all, it may be using properties or
> __getattr__ etc. to implement computed attributes. All sorts of things
> can go wrong when copying arbitrary instances. That's why there is an
> entire protocol so that types can make themselves copyable.
>
> Matt, if you're trying to make a "copy any instance" utility function, you
> are re-inventing the wheel. See the copy module instead.

Right, I overlooked classes with __slots__ and that override __new__
and other special methods. copy() is the better and more customizable
solution. This is the same route for customizing the pickle of an
object, so the pickle docs should help:

http://docs.python.org/library/pickle.html#pickling-and-unpickling-normal-class-instances

The default __reduce__(2) will call  __getnewargs__ and __getstate__
to create an info tuple used to construct a copy. The args from
__getnewargs__ are used to call a __newobj__ function that in turn
calls cls.__new__(cls, *args) to create a new instance. The copy
reconstructor will update the new instance with the state from
__getstate__ if it's non-false. If the state is a tuple, the 2nd item
should be the slots state. The reconstructor uses __setstate__ if it
exists. Otherwise it updates the instance dict with state[0] if it's
non-false, and uses setattr to set slots if state[1] is non-false.

class Test(object):
   def __new__(cls, v):
       print '__new__', v
       obj = object.__new__(cls)
       obj.new_attr = v
       return obj

   def __getnewargs__(self):
       print '__getnewargs__'
       return (self.new_attr, )

   def __getstate__(self):
       print '__getstate__'
       return ({'attr': 'value'}, {'slot_attr': 'value'})

   def __setstate__(self, state):
       print '__setstate__', state

>>> t1 = Test('test')
__new__ test
>>> t2 = copy.copy(t1)
__getnewargs__
__getstate__
__new__ test
__setstate__ ({'attr': 'value'}, {'slot_attr': 'value'})


More information about the Tutor mailing list