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)