[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