elegant way to have an almost-copy-constructor that allows to update some members

点点圈 提交于 2020-01-05 04:04:50

问题


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!