我试图通过改变一个属性的值来修改一个已经定义的类。重要的是,我希望这个改变在内部传播。
例如,考虑以下类:
class Base:
x = 1
y = 2 * x
# Other attributes and methods might follow
assert Base.x == 1
assert Base.y == 2
我想将x
更改为2
,使其等效于此。
class Base:
x = 2
y = 2 * x
assert Base.x == 2
assert Base.y == 4
但我想以如下方式提出:
Base = injector(Base, x=2)
有没有一种方法可以做到这一点,而无需重新编译原来的类源代码?
2条答案
按热度按时间t0ybt7op1#
由于
Base
类是用dependent动态赋值属性声明的,所以这个想法比较复杂,虽然我们可以检查类的静态属性,但是我认为除了解析类的sourcecode
之外,没有其他方法可以得到动态表达式。查找并替换“injected”属性名,使用其值和exec/eval
重新定义。但这是您希望避免的方式。(此外:如果您期望injector
对于所有类都是统一的)。如果您希望继续依赖于动态计算的属性,请将依赖属性定义为
lambda
函数。hgc7kmma2#
您想要达到的效果属于“React式编程”的范畴--一种编程范式(现在无处不在的Javascript库就是从这种编程范式中得到灵感而得名的)。
虽然Python有很多机制可以实现这一点,但你需要编写代码来真正 * 利用**这些机制。
默认情况下,你在示例中使用的普通Python代码使用的是命令式范式,它是急切的:每当遇到表达式时,就执行该表达式,并使用该表达式的结果(在这种情况下,结果存储在class属性中)。
Python的优势还在于,一旦你编写了一个允许一些被动代码发生的代码库,你的代码库的 * 用户 * 就不必意识到这一点,事情或多或少会“神奇地”运行。
但是,如上所述,这不是免费的。对于能够在
x
发生变化时重新定义y
的情况,有两条路径可以遵循-最重要的是,在执行“*”操作符时(当Python * 解析 * 类体时就会发生这种情况),至少操作的一端不再是普通的数字,而是一个实现定制
__mul__
方法的特殊对象然后,不是将结果数存储在y
中,而是将表达式存储在某个地方,并且当y
作为类属性被检索时,其他机制强制表达式解析。如果你想在instance级别而不是类级别实现,那么它会更容易实现,但是要记住,你必须在你的特殊“source”类中为原始值定义 each 操作符。
此外,这种方法和使用
property
的更简单的示例描述符方法都是“延迟求值”的:这意味着,y
的值是在使用时计算的(如果它将被多次使用,则可以缓存)。如果您希望在每次分配x
时(而不是在使用y
时)对其求值,则需要其他机制。虽然缓存,但惰性方法可以将对急切求值的需要减少到不需要的程度。1 -在那里挖掘之前
Python编写这样的代码最简单的方法就是把要计算的表达式写成函数,然后使用
property
内置函数作为描述符来获取这些值,缺点很小:你只需要把表达式 Package 在一个函数中(然后,把这个函数 Package 在一个可以给它添加描述符属性的东西中,比如property
)。你可以在你的表达式中自由地使用任何Python代码,包括函数调用、对象示例化、I/O等等。2(注意,另一种方法需要连接每个所需的操作符,仅仅是为了入门)。让您想要的东西为Base的 * 示例 * 工作的简单“101”方法是:
property
的工作可以重写,以便从类中检索y
,而不是从示例中检索,也可以达到同样的效果(这仍然比其他方法更容易)。如果这样做对您有用,我建议您这样做,如果您需要缓存
y
的值直到x
实际发生变化,可以使用普通编码来完成2 -正是您所要求的,带有元类
如上所述,在计算
2 * x
表达式时,Python需要知道y
属性的特殊状态。在赋值时,这已经太晚了。幸运的是,Python 3允许类体在 custom 命名空间中运行,以进行属性赋值,方法是在元类中实现__prepare__
方法,然后记录所有发生的事情。以及用实现__mul__
和其他特殊方法的特制对象替换感兴趣的原始属性。这样做甚至可以让值被急切地计算出来,这样它们就可以像普通的Python对象一样工作,但是要注册信息,这样一个特殊的
injector
函数就可以重新创建类,重做所有依赖于表达式的属性,它还可以实现惰性求值,就像上面描述的那样。我可以做,但要花上几个小时--这比我能提供的S. O.答复要多,抱歉。
3 -将你的原始值 Package 在注册它们的基类中
这样
injector
就可以工作了,只是不要用__prepare__
方法来做元类,并要求将基元值(如本例中的x
) Package 在一个专门的类中,这个类将创建惰性操作符,并跟踪所有操作。但是保存某些复杂性保存在那里,它几乎相同的工作量的另一种方法。
如果你真的需要这个,而不是
property
的方法,取得联系(甚至通过评论)