33

当我试图让类装饰器和方法装饰器很好地结合在一起时,我遇到了这种行为。本质上,方法装饰器会将一些方法标记为特殊的并带有一些虚拟值,而类装饰器会在之后出现并稍后填充该值。这是一个简化的例子

>>> class cow:
>>>     def moo(self):
>>>         print 'mooo'
>>>     moo.thing = 10
>>>
>>> cow.moo.thing
10
>>> cow().moo.thing
10
>>> cow.moo.thing = 5
AttributeError: 'instancemethod' object has no attribute 'thing'
>>> cow().moo.thing = 5
AttributeError: 'instancemethod' object has no attribute 'thing'
>>> cow.moo.__func__.thing = 5
>>> cow.moo.thing 
5

有谁知道为什么cow.moo.thing = 5不起作用,即使cow.moo.thing很清楚地给了我 10?为什么cow.moo.__func__.thing = 5有效?我不知道它为什么会这样,但是在随机摆弄dir(cow.moo)列表中的东西试图让它工作时,它突然做到了,我不知道为什么。

4

2 回答 2

36

对于属性查找,Python 会自动为您使用附加到实例方法的真实函数。

对于属性设置,它不是。

它们是两个独立的操作,具体取决于您在语句的哪一侧,即使它们都使用.运算符。

当您访问实例方法__func__时,您正在手动访问实际具有该moo属性的真实函数。

在 Python 3 中,这将按照您的意愿/期望工作,因为方法基本上只是函数。

于 2011-08-11T23:23:30.013 回答
3

如果您希望从 C 中修改函数和实例方法的函数属性,那么您必须检查您拥有的可调用对象的类型。

因此,假设您有某种类型的可调用 PyObject,您可以像这样检查它:

PyObject *callable;  // set to something callable
PyObject *setting;  // set to something
if(PyMethod_Check(callable)){
    PyObject_SetAttrString(PyMethod_Function(callable),"attribute",setting);
}else{
    PyObject_SetAttrString(callable,"attribute",setting);
}
...
// and the inverse
if(PyMethod_Check(callable){         
     if(PyObject_HasAttrString(PyMethod_Function(callable),"attribute")){
        PyObject_DelAttrString(PyMethod_Function(callable),"attribute");
     }
  }else{
     if(PyObject_HasAttrString(callable,"attribute")){
        PyObject_DelAttrString(callable,"attribute");
     }
  }  

现在,agf 指出的代码在 Python 中适用于实例方法。如果我只是尝试设置实例方法的属性,无论我如何尝试从 Python 访问它,它都找不到该属性。

我遇到了这个问题,李浩毅的问题和 agf 的回答帮助我理解了需要更改的内容。我想有人会在寻找如何通过 C 解决这个问题时找到这个问题并再次回答。

编辑: 注意:这适用于 Python 2.7.x。Python 3.x 使用不同的函数调用。

于 2012-12-02T15:03:35.117 回答