Question

I would like to create an instance method based on a function, as a parameter to instance initialization, as follows:

from traits.api import *

class C(HasTraits):
    f = Function

c = C(f=lambda self: self)

However, if I run

c.f()

it says (not in any way surprisingly)

TypeError: <lambda>() takes exactly 1 argument (0 given)

Changing f from Function to Method does not help as it does not accept the function argument (requires instancemethod).

So my question is, how I could get custom function act as instance method (with self parameter as ordinary instance methods) when using Traits?

Was it helpful?

Solution 2

Define new Trait type MyMethod as follows:

import types
class MyMethod(TraitType):
    def validate(self, object, name, value): 
        if isinstance(value, types.MethodType):
            return value
        if isinstance(value, types.FunctionType):
            return types.MethodType(value, object, object.__class__)
        self.error(object, name, value)

and use it instead of Function or Method in the previous code, and functions will be converted to instancemethods automatically.

OTHER TIPS

A function—whether defined with lambda or def—is not a method.

You can't just change its type; a method has more information in it than a function—it's bound to an object. That's how Python knows what to pass for the first (self) parameter.

In your case, you're looking for an unbound method, which isn't bound to an object—but it's still bound to a class.

In normal cases (when you def a method inside a class definition, then create an instance of the class, then call a method on it), this happens automatically for you. (See How methods work for some details.) But when you're doing it all yourself, you need to create the bound method yourself.

As with any other type in Python, you do this by calling the type as a constructor. The problem is that "bound method" isn't accessible as a builtin type. That means you have to go to the types module, which "defines names for some object types that are used by the standard Python interpreter". Like this:

bound = types.MethodType(func, my_obj, type(my_obj))

unbound = types.UnboundMethodType(func, None, my_type)

But notice that there's often no good reason to do this—you could just as easily cram the object into the closure. These callables will all do the same thing:

func = lambda self: self
bound = types.MethodType(func, my_obj, type(my_obj))

func2 = lambda: my_obj

func3 = lambda self: self
unbound = types.UnboundMethodType(func, None, my_type)
func3a = lambda: unbound(my_obj)    

Two last thoughts:

If you understand about descriptors, the magic that makes @property work and the magic that makes bound methods work will interfere with each other. I don't know if that's true with Enthough traits, but it might. So, the answer to your question could well turn out to be "you can't do that". Instead, you will have to create a Function trait, and then a wrapper method that calls that trait explicitly.

If you use Python 3.x instead of 2.x, unbound methods go away (classes just use plain old functions), so a lot of the complexity you're hitting never arises.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top