Adding attributes to instancemethods in Python
Asked Answered
A

2

34

I bumped into this behaviour when trying to get class-decorators and method-decorators to play nicely together. Essentially, the method decorators would flag some of the methods as special with some dummy value, and the class decorator would come by after and fill in the value later. This is a simplified example

>>> 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

Does anyone know why cow.moo.thing = 5 does not work, even though cow.moo.thing quite clearly gives me 10? And why cow.moo.__func__.thing = 5 works? I have no idea why it does, but in randomly fiddling with stuff in the dir(cow.moo) list trying to get something to work it suddenly did, and i have no idea why.

Aylsworth answered 11/8, 2011 at 23:17 Comment(0)
Z
37

For attribute lookup, Python is automatically using the real function attached to the instance method for you.

For attribute setting, it is not.

They are two separate operations depending on which side of the statement you're on, even though they both use the . operator.

When you access an instance method's __func__, you're manually accessing the real function that actually has the moo attribute.

In Python 3 this will work as you would like / expect as methods are basically just functions.

Zealotry answered 11/8, 2011 at 23:23 Comment(4)
Sorry for reviving an old post but in Python3 cow().moo.thing = 5 is returning AttributeError: 'method' object has no attribute 'thing'. Do you know why ?Fleabag
@JacquesGaudin On the instance (cow().moo) the function is wrapped in a method object in any version of Python -- the difference is on the class (cow.moo) it's not wrapped in Python 3. The wrapping is necessary for self to get passed automatically to the function.Zealotry
Thanks, that explains why I was having trouble adding attributes to an instance method on the fly.Fleabag
How do you set the attribute to an instance method then?Commemoration
N
3

If you are looking to modify the function attributes of both functions and instance methods from C then you have to check the type of callable you have.

So assuming you have a PyObject of some type of callable you can check it like this:

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");
     }
  }  

Now the code agf pointed out works from within Python for instance methods. If I just try to set the attribute of the instance method it would not find the attribute no matter how I tried to access it from Python.

I ran into this issue and Li Haoyi's question with agf's answer helped me understand what needed to be changed. I figured someone will find this question and answer again while looking for how to solve this issue through C.

Edit: Note: This is for Python 2.7.x. Python 3.x uses different function calls.

Negative answered 2/12, 2012 at 15:3 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.