[Tutor] overriding instance attributes with keywords

Steven D'Aprano steve at pearwood.info
Tue Aug 14 04:28:44 CEST 2012


On 14/08/12 07:16, eryksun wrote:
> On Mon, Aug 13, 2012 at 3:13 PM, Gregory, Matthew
> <matt.gregory at oregonstate.edu>  wrote:
>>
>> I'm trying to create a new instance from an existing instance
>
>> def new_with_overrides(s1, **kwargs):
>>      new_params = {'a': s1.a, 'b': s1.b}
>>      for (k, v) in kwargs.iteritems():
>>          if k in new_params:
>>              new_params[k] = v
>>      return Spam(new_params['a'], new_params['b'])
>
>> This works but it doesn't seem very extendable if new attributes are added to Spam.
>
> In general instance attributes won't map to __init__ arguments. You
> can create a new instance by calling  __new__. Then update from the
> old dict and add in override attributes.
>
> 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.

If you're looking to make an short-cut for your own class, my recommendation
is that you give your class two methods:

# Your class MUST inherit from object or some other builtin for this to work
class MyClass(object):
     def to_mapping(self):
         """Return a dict of whatever arguments created the instance."""
         return {"a": self.a, ...}
     @classmethod
     def from_mapping(cls, adict, **kwargs):
         d = dict(adict, **kwargs)
         return cls(**d)


Typical usage:

x = MyClass(a=1, b=2, c=3)
y = x.from_mapping(x.to_mapping(), a=999)




-- 
Steven


More information about the Tutor mailing list