1

我在解决我正在研究的问题时遇到了一些问题。我有一组专门的函数,它们将在整个程序中使用,它们基本上是可以替换函数和方法的动态可调用函数。由于需要让它们正常工作以模拟方法的功能,这些函数重写__get__以提供一个包装版本,以提供对检索对象的访问权限。

不幸的是,__get__如果函数直接在实例上设置,则不起作用。这是因为在实例中找到键时,只有“数据描述符”才会调用该__get__函数。__dict__想到的唯一解决方案是:欺骗 python 认为这是一个数据描述符。这涉及在描述符上创建一个__set__函数。理想情况下,我希望这个__set__函数作为传递(将控制权返回给调用者并继续评估,就好像它不存在一样)。

有什么方法可以欺骗 python 认为描述符是数据描述符,但让包含的类/实例仍然能够正常使用它的 setattr 命令?

另外,我知道可以通过覆盖__getattribute__调用者来做到这一点。但是,这是一个糟糕的解决方案,因为我必须对内置的“对象”和任何覆盖它的东西执行此操作。不完全是一个很好的解决方案。

或者,如果有任何替代解决方案,我很乐意听到。

这是一个问题示例:

class Descriptor(object):  
    def __get__(self, obj, objtype = None):  
        return None  

class Caller(object):  
    a = Descriptor()  

print a  
>>> None  
x = Caller()  
print a
>>> None
x.a = Descriptor()
print x.a
>>> <__main__.Descriptor object at 0x011D7F10>  

最后一种情况应打印“无”以保持一致性。

如果您将 a 添加__set__到描述符,这将打印“无”(根据需要)。但是,这会使 xa = (some value) 的任何命令无法像以前一样工作。由于我不想弄乱此功能,因此没有帮助。任何解决方案都会很棒。

更正:我之前的想法仍然行不通,因为我稍微误解了描述符处理。显然,如果一个描述符根本不在一个类上,它将永远不会被调用——不管set。我的条件只有在有一个 dict val 和一个同名的类访问器时才有帮助。我实际上正在寻找一个更类似于以下内容的解决方案:http: //blog.brianbeck.com/post/74086029/instance-descriptors但这并不涉及让阳光下的一切都继承一个专门的接口。

不幸的是,鉴于对描述符接口的这种新理解,这可能是不可能的?为什么哦,为什么 python 会使装饰器本质上是非动态的?

4

2 回答 2

1

我认为最干净的解决方案是不理会__set__,并在类上设置描述符——如果需要,包装原始类。即,代替x.a = Descriptor(),在哪里做setdesc(x, 'a', Descriptor()

class Wrapper(object): pass

def setdesc(x, name, desc):
  t = type(x)
  if not issubclass(t, wrapper):
    class awrap(Wrapper, t): pass
    x.__class__ = awrap
  setattr(x.__class__, name, desc)

这是我建议的一般方法,当有人想要“在实例上设置”任何需要在类上设置才能工作但又不想影响实例的原始类的东西(特殊方法或描述符)时。

当然,只有当你有新样式的类时,它才能正常工作,但是描述符无论如何都不能很好地与旧样式的类一起使用;-)。

于 2009-08-16T04:45:30.743 回答
0

我想我可能对我的问题有一个答案,尽管它并不是那么漂亮——它确实回避了这个问题。我目前的攻击计划是做 python 所做的事情——手动绑定函数。我已经在使用我的未绑定函数的get命令来生成绑定类型的函数。一种可能的解决方案是强制任何想要设置新功能的人手动绑定它。这很烦人,但并不疯狂。Python 实际上让你这样做(如果你只是将一个函数作为属性设置到一个实例上,它不会被绑定)。

让这种情况自动发生仍然很好,但强迫正在设置新函数的人使用 xa = Descriptor() 并不可怕。get (x) 在这种情况下将给出所需的行为(以及例如,就此而言)。这不是一个通用的解决方案,但它适用于这个有限的问题,基本上是在模拟方法绑定。话虽如此,如果有人有更好的解决方案,我仍然很高兴听到它。

于 2009-08-16T05:51:31.520 回答