Python 重新创建类对象更改一个构造函数参数

Python 重新创建类对象更改一个构造函数参数,python,python-3.x,class,object,Python,Python 3.x,Class,Object,我希望在我的类中实现一个update方法,该方法重新创建类,但只更改一个构造函数参数 我的尝试: class Updateable: def update(self, var, var_str, **kwargs): kwargs.update(self._vars) kwargs[var_str] = var self.__init__(**kwargs) class Rectangle(Updateable): def __i

我希望在我的类中实现一个
update
方法,该方法重新创建类,但只更改一个构造函数参数

我的尝试:

class Updateable:
    def update(self, var, var_str, **kwargs):
        kwargs.update(self._vars)
        kwargs[var_str] = var
        self.__init__(**kwargs)

class Rectangle(Updateable):
    def __init__(self, length, perimeter):
        self._vars = locals()
        self.length = length
        self.width = 0.5*(perimeter - 2.*length)      

r = Rectangle(10, 20)
r.update('perimeter', 16)
问题是,我认为整个
locals()


实现此功能的正确方法是什么?装饰者,元类?更简单的问题?

如果我误解了你的问题,或者如果你不想得到高水平的建议和公正的问题解决,请纠正我

\uuuu init\uuuu
当前所做的是,如果(可能相关的)变量发生更改,则重新计算几何形状的属性。步骤1是将其从
\uuuu init\uuu
中取出,并放入一个单独的
def
,由init调用。这里的主要内容是,您不向该函数传递变量,而是使用在
\uuuu init\uuuu
或超类更新方法中设置的类变量

第2步是更改更新功能。 Python有一种称为getter和setter的形式,允许您将任务挂接到更新变量上。另一方面,更通用的方法更类似于您自己的更新,并在下面的选项2中列出

示例替代方案
正如其他人所观察到的,像
width
这样的计算应该转移到属性或方法;它们不属于初始化器

如果确实要返回一个新实例,这将适用于最简单的情况,其中实例的属性是不可变的对象,如字符串或整数:

import copy

class Copyable:

    """Mixin to create copies with a changed attribute."""

    def copy_and_modify(self, var, var_str, **kwargs):
        new = copy.copy(self)
        setattr(new, var, var_str)
        return new

class Rectangle(Copyable):

    def __init__(self, length, perimeter):
        self.perimeter = perimeter
        self.length = length

    @property
    def width(self):
        return 0.5 * (self.perimeter - 2.0 * self.length)
但是,如果您的对象包含嵌套的可变结构,如字典或列表,则需要更改
copy\u和
以使用
copy.deepcopy
(但请注意,深度复制很慢)


你可以在你的子类中定义
\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu
\uuuuuuuuuuu deepcopy\uuuuuuuuuuuuuuuuuu
方法来微调对复制过程的控制。

Laurens Koppenol建议使用
属性
(Python对Comp这是一个好主意,但他的示例代码在很多方面都是不完整的,而且比必须的更复杂,因此这里有一个更简单、有效和pythonic的示例(不需要
可更新的
类,也不需要任何其他无关的东西):

如果要缓存
宽度
值(以避免无用的计算),但仍要确保在
长度
周长
更改时对其进行更新,则需要将其设置为所有属性:

class Rectangle(object):
    def __init__(self, length, perimeter):
        self.length = length
        self.perimeter = perimeter

    @property
    def length(self):
        return self._length

    @length.setter
    def length(self, value):
        self._length = value
        self._width = None


    @property
    def perimeter(self):
        return self._perimeter

    @length.setter
    def perimiter(self, value):
        self._perimeter = value
        self._width = None

    @property
    def width(self):
        if self._width is None:
            self._width = 0.5*(self.perimeter - 2.*self.length)     
        return self._width
或者(如果您有很多这样的东西)使用一些“cached_property with invalidate”实现,如下所示:

编辑:wrt/你的问题,调用
局部变量
确实很难看(而且很容易中断-你可能有不应该是
\u vars
的一部分的局部变量),以及需要在子类中明确设置
self.\u vars
。另外,
update()
API本身也非常难看。现在,您不需要任何花哨的东西来让整个事情变得更加pythonic-这里有一个解决方案,它的唯一样板是需要调用
可更新。uuu init_uuu
和命名参数(不适用于位置参数):


作为旁注,我个人发现非常令人不安的是,
Rectangle
类使用了一个
periment
参数,但存储了一个
width
。。。也许你应该考虑一个<代码>周长< /代码>属性?(即使是只读的,以避免重新计算etc)

在对象已经存在之后调用
\uuu init\uuu
,似乎有点奇怪。为什么不使用一个
重新计算\u width
方法或其他方法呢?因为我希望它适用于我拥有的其他类,它们都有不同的参数名称。有时它是
宽度
,有时它是
角度
,有时它是
波长
,可以是任何东西。1/不要对实现属性使用双下划线-这会触发名称混乱函数,从而破坏继承。仅使用一个前导下划线。2/如果使用继承,则调用超类初始值设定项。3/在Python2.x中,属性不能与旧式类一起正常工作。4/您的“可更新”类应该使用默认实现定义“重新计算所有内容()”,或者是一个抽象基类(参见stlib的ABC模块)。5/您在
矩形中有一个名称错误。uuu init_uuu
(…)和6。在初始值设定项中手动设置实现属性,而不是使用属性设置器,这有点挫败了拥有属性的全部意义。哦,也是的:
updateable
的意义是通用的-它应该对特定的实现属性一无所知。谢谢你的建设性批评,谢谢你的第二个建议例如,您的意思是在初始化方法中分配给
self.\u length/\u permiture
?问题是,我想将更新应用于许多类,所有这些类都有不同的参数,我想避免将每个参数都作为
属性写入。这就是为什么我试图创建一个可以实现这一点的泛型类。不确定这是否可行。@snakecharmerb不,我是指我写的:分配给
self.length
self.permiture
。为什么我要绕过我的属性设置器并打破
宽度
getter代码?@Jean-Luc yes sorry-cf我编辑的答案确实解决了您的问题。但是我不能保证它永远不会破坏任何东西……如果它创建了一个新实例,那么该方法不应该被命名为
update()
@brunodesshuilliers。我遵守OP的原始约定,但你是对的。OP正在重新初始化现有实例,而不是创建一个新实例。今天晚些时候我会确定这个名字<代码>创建\修改\复制
可能吗?另外,我将把mixin重命名为与purpos更接近的东西
class Copyable:

    def copy_and_modify(self, var, var_str, **kwargs):
        new = copy.deepcopy(self)
        setattr(new, var, var_str)
        return new
class Rectangle(object):
    def __init__(self, length, perimeter):
        self.length = length
        self.perimeter = perimeter

    @property
    def width(self):
        return 0.5*(self.perimeter - 2.*self.length)     
class Rectangle(object):
    def __init__(self, length, perimeter):
        self.length = length
        self.perimeter = perimeter

    @property
    def length(self):
        return self._length

    @length.setter
    def length(self, value):
        self._length = value
        self._width = None


    @property
    def perimeter(self):
        return self._perimeter

    @length.setter
    def perimiter(self, value):
        self._perimeter = value
        self._width = None

    @property
    def width(self):
        if self._width is None:
            self._width = 0.5*(self.perimeter - 2.*self.length)     
        return self._width
class Updateable(object):
    def __init__(self, **kwargs):
        self._vars = kwargs

    def update(self, **kwargs):
        vars = self._vars.copy()
        vars.update(**kwargs)
        self.__init__(**vars)


class Rectangle(Updateable):
    def __init__(self, length, perimeter):
        super(Rectangle, self).__init__(length=length, perimeter=perimeter)
        self.length = length
        self.width = 0.5*(perimeter - 2.*length)

r = Rectangle(10, 20)
r.update(perimeter=40)