问题
i have an instance of a container class that serves as basis for several other instances. it is called data0
in the example. i would like to be able to make copies of that and then update some of the members of the copies.
yes, that could be done using the copy
module, creating a copy and assigning the new values for the members. but what if the container class is immutable?
i was hoping to be able to implement what is needed to turn the class into a mapping (keys
and __getitem__
) and make __init__
accept the base class plus some modifications. this was probably inspired by the way you can update a dictionary:
x = {'a': 0, 'b': 1, 'c': 2}
y = {'b': 7}
z = {**x, 'b': 7}
print(z) # {'a': 0, 'b': 7, 'c': 2}
so what i would like to be able to do is this:
data3 = Container(**data0, b=7)
print(data3)
here is what i tried:
class Container:
KEYS = ('a', 'b', 'c')
def __init__(self, a=None, b=None, c=None):
self._a = a
self._b = b
self._c = c
@property
def a(self):
return self._a
@property
def b(self):
return self._b
@property
def c(self):
return self._c
def keys(self):
return Container.KEYS
def __getitem__(self, key):
if key not in Container.KEYS:
raise KeyError(key)
return getattr(self, key)
def __str__(self):
return f'{self.__class__.__name__}(a={self.a}, b={self.b}, c={self.c})'
data0 = Container(a=1, b=2, c=3)
print(data0) # Container(a=1, b=2, c=3)
data1 = Container(**data0) # 'copy constructor'
print(data1) # Container(a=1, b=2, c=3)
data2 = Container(**{**data0, 'b':7})
print(data2) # Container(a=1, b=7, c=3)
the last method works but is ugly...
as stated above: what i would like to be able to do is this:
data3 = Container(**data0, b=7)
print(data3)
but this (understandably) fails with the error message
TypeError: type object got multiple values for keyword argument 'b'
how can i fix that? or is there a more obvious way to create a new instance from a given basis with updated members?
or is the standard way to go just to inherit from that class with the desired values as defaults?
class Container1(Container):
def __init__(self, a=1, b=2, c=3):
super().__init__(a=a, b=b, c=c)
data3 = Container1(b=7)
print(data3) # Container1(a=1, b=7, c=3)
this is a bit verbose and has the drawback that data3
is of the type Container1
...
my attempt behaves differently on python 3.5 than on python 3.6 as described/asked here.
来源:https://stackoverflow.com/questions/50375793/elegant-way-to-have-an-almost-copy-constructor-that-allows-to-update-some-member