是否有可能超载Python的分配?是否有可能超载Python的分配?(Is it possible

2019-06-01 02:07发布

有没有一种神奇的方法,可以重载赋值运算符,如__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()

可能吗? 是不是疯了吗? 我应该是药吗?

Answer 1:

你描述它的方式是绝对不可能的。 分配给一个名字是一个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", False) and name == "x":
            raise AttributeError("MyClass does not allow assignment to .x member")
        self.__dict__[name] = value

>>> m = MyClass(3)
>>> m.x
3
>>> m.x = 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

请注意,有一个成员变量, _locked ,其控制所述分配是否被允许。 你可以解锁更新值。



Answer 2:

不,分配是一个语言内在的不具有修改钩。



Answer 3:

我不认为这是可能的。 我看到它的方式,分配给一个变量不对它做任何事情之前引用的对象:它只是变“点”,以不同的对象了。

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__()方法是抛出一个异常,并保持“不变”里面的东西。



Answer 4:

没有没有

想想看,在你的例子你重新绑定名称VAR为新值。 您还没有实际接触的保护实例。

如果你想重新绑定的名称实际上是在一些其他实体即myobj.var属性,那么你可以阻止值分配给实体的属性/属性。 但我相信这是你从你的例子想不是。



Answer 5:

在全局命名空间,这是不可能的,但你可以利用更先进的Python元编程,以防止在的多个实例Protect对象被创建。 该Singleton模式是这个很好的例子。

在辛格尔顿的情况下,你将确保一旦实例化,即使原始变量引用实例重新分配,该对象会持续存在。 任何后续实例将只返回到同一对象的引用。

尽管这种模式,你将永远无法阻止一个全局变量的名字本身被重新分配。



Answer 6:

使用顶级命名空间,这是不可能的。 当您运行

var = 1

它存储键var和值1在全局字典。 它是大致相当于调用globals().__setitem__('var', 1) 问题是,你不能替换正在运行的脚本的全局字典(你或许可以通过与叠搞乱,但是这不是一个好主意)。 然而,你可以在二级命名空间中执行代码,并提供自定义字典的全局。

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 val 。 你也可以使用execfile(filename, myg) 请注意,这不能防止恶意代码。



Answer 7:

一个丑陋的解决方案是重新分配的析构函数。 但它没有真正的超负荷作业。

import copy
global a

class MyClass():
    def __init__(self):
            a = 1000
            # ...

    def __del__(self):
            a = copy.copy(self)


a = MyClass()
a = 1


Answer 8:

是的,这是可能的,你可以处理__assign__通过修改ast

pip install assign

测试用:

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



Answer 9:

一般来说,我发现是压倒最好的办法__ilshift__作为setter和__rlshift__作为一个getter,由物业装饰复制。 这几乎是最后的算刚刚被解决(|&^)和逻辑较低。 它很少被使用( __lrshift__较少,但它可以采取帐户)。

在使用的PyPI的只能向前分配可控制分配包,所以实际的“实力”运营商的更低。 分配的PyPI包例如:

class Test:

    def __init__(self, val, name):
        self._val = val
        self._name = name
        self.named = False

    def __assign__(self, other):
        if hasattr(other, 'val'):
            other = other.val
        self.set(other)
        return self

    def __rassign__(self, other):
        return self.get()

    def set(self, val):
        self._val = val

    def get(self):
        if self.named:
            return self._name
        return self._val

    @property
    def val(self):
        return self._val

x = Test(1, 'x')
y = Test(2, 'y')

print('x.val =', x.val)
print('y.val =', y.val)

x = y
print('x.val =', x.val)
z: int = None
z = x
print('z =', z)
x = 3
y = x
print('y.val =', y.val)
y.val = 4

输出:

x.val = 1
y.val = 2
x.val = 2
z = <__main__.Test object at 0x0000029209DFD978>
Traceback (most recent call last):
  File "E:\packages\pyksp\pyksp\compiler2\simple_test2.py", line 44, in <module>
    print('y.val =', y.val)
AttributeError: 'int' object has no attribute 'val'

轮班一样:

class Test:

    def __init__(self, val, name):
        self._val = val
        self._name = name
        self.named = False

    def __ilshift__(self, other):
        if hasattr(other, 'val'):
            other = other.val
        self.set(other)
        return self

    def __rlshift__(self, other):
        return self.get()

    def set(self, val):
        self._val = val

    def get(self):
        if self.named:
            return self._name
        return self._val

    @property
    def val(self):
        return self._val


x = Test(1, 'x')
y = Test(2, 'y')

print('x.val =', x.val)
print('y.val =', y.val)

x <<= y
print('x.val =', x.val)
z: int = None
z <<= x
print('z =', z)
x <<= 3
y <<= x
print('y.val =', y.val)
y.val = 4

输出:

x.val = 1
y.val = 2
x.val = 2
z = 2
y.val = 3
Traceback (most recent call last):
  File "E:\packages\pyksp\pyksp\compiler2\simple_test.py", line 45, in <module>
    y.val = 4
AttributeError: can't set attribute

因此, <<=内的财产中获得价值运营商是更加视觉上洁净解决方案,它不试图用户做一些反射错误,如:

var1.val = 1
var2.val = 2

# if we have to check type of input
var1.val = var2

# but it could be accendently typed worse,
# skipping the type-check:
var1.val = var2.val

# or much more worse:
somevar = var1 + var2
var1 += var2
# sic!
var1 = var2


Answer 10:

里面一个模块,这是绝对有可能的,通过一点点黑暗魔法。

import sys
tst = sys.modules['tst']

class Protect():
  def __assign__(self, value):
    raise Exception("This is an ex-parrot")

var = Protect()  # once assigned...

Module = type(tst)
class ProtectedModule(Module):
  def __setattr__(self, attr, val):
    exists = getattr(self, attr, None)
    if exists is not None and hasattr(exists, '__assign__'):
      exists.__assign__(val)
    super().__setattr__(attr, val)

tst.__class__ = ProtectedModule

请注意,即使从模块中,你不能一次班级发生变更写保护变量。 上面的例子假设的代码驻留名为模块中tst 。 你可以在做这个repl通过改变tst__main__



文章来源: Is it possible to overload Python assignment?