是否有可能超载Python分配?
是否有一个神奇的方法,可以重载赋值运算符,如__assign__(self, new_value)
?
我想禁止重新绑定一个实例:
class Protect(): def __assign__(self, value): raise Exception("This is an ex-parrot") var = Protect() # once assigned... var = 1 # this should raise Exception()
可能吗? 疯了吗? 我应该吃药吗?
你描述的方式是绝对不可能的。 赋值给一个名字是Python的一个基本特征,并没有提供任何钩子来改变它的行为。
但是,通过覆盖.__setattr__()
, 可以根据需要控制对类实例中成员的赋值。
class MyClass(object): def __init__(self, x): self.x = x self._locked = True def __setattr__(self, name, value): if self.__dict__.get("_locked") and name == "x": raise AttributeError, "MyClass does not allow assignment to .x member" self.__dict__[name] = value >>> m = MyClass(3) >>> mx 3 >>> mx = 4 Traceback (most recent call last): File "<stdin>", line 1, in <module> File "<stdin>", line 7, in __setattr__ AttributeError: MyClass does not allow assignment to .x member
请注意,有一个成员variables_locked
,用于控制赋值是否被允许。 你可以解锁它来更新值。
不,因为作业是一种没有修改钩子的语言内在 。
我不认为这是可能的。 我看到它的方式,赋值给一个variables不会对之前提到的对象做任何事情:只是variables“指向”一个不同的对象。
In [3]: class My(): ...: def __init__(self, id): ...: self.id=id ...: In [4]: a = My(1) In [5]: b = a In [6]: a = 1 In [7]: b Out[7]: <__main__.My instance at 0xb689d14c> In [8]: b.id Out[8]: 1 # the object is unchanged!
但是,您可以通过使用__setitem__()
或__setattr__()
方法创build包装对象来模仿所需的行为,从而引发exception,并将“不可更改”的内容保留在内部。
没有没有
考虑一下,在你的例子中,你正在重新命名var为一个新的值。 你实际上并没有触及Protect的实例。
如果您希望重新绑定的名称实际上是某个其他实体的属性,即myobj.var,那么您可以防止为该实体的属性/属性分配一个值。 但是我认为这不是你想要的。
在全局命名空间中,这是不可能的,但是您可以利用更高级的Python元编程来防止创buildProtect
对象的多个实例。 Singleton模式就是一个很好的例子。
在Singleton的情况下,您可以确保一旦实例化,即使引用实例的原始variables被重新分配,该对象也会保留。 任何后续的实例只会返回对同一个对象的引用。
尽pipe这种模式,你永远不能防止全球variables名称被重新分配。
使用顶级命名空间,这是不可能的。 当你跑步
`var = 1`
它将关键字var
和值1
在全局字典中。 这大致相当于调用globals().__setitem__('var', 1)
。 问题是你不能在一个正在运行的脚本中replace全局字典(你可能可以通过乱搞堆栈,但这不是一个好主意)。 但是,您可以在辅助命名空间中执行代码,并为其全局提供自定义字典。
class myglobals(dict): def __setitem__(self, key, value): if key=='val': raise TypeError() dict.__setitem__(self, key, value) myg = myglobals() dict.__setitem__(myg, 'val', 'protected') import code code.InteractiveConsole(locals=myg).interact()
这将触发一个几乎正常运行的REPL,但拒绝尝试设置variablesval
。 你也可以使用execfile(filename, myg)
。 请注意,这不能防止恶意代码。
一个丑陋的解决scheme是重新分配的析构函数。 但这不是真正的超载分配。
import copy global a class MyClass(): def __init__(self): a = 1000 # ... def __del__(self): a = copy.copy(self) a = MyClass() a = 1
是的,这是可能的,你可以通过修改ast
处理__assign__
。
pip install assign
testing:
class T(): def __assign__(self, v): print('called with %s' % v) b = T() c = b
你会得到
>>> import magic >>> import test called with c
该项目是在https://github.com/RyanKung/assign
更简单的要点: https://gist.github.com/RyanKung/4830d6c8474e6bcefa4edd13f122b4df
: https://gist.github.com/RyanKung/4830d6c8474e6bcefa4edd13f122b4df