2

All is in the title. I'd like to create a class method and a class attribute, both constructed only once, when the class is created, using the first in the second's definition.

With my best try, I just get a TypeError: 'classmethod' object is not callable.

Here is my code :

import numpy as np

class Foo( object ) :

    @classmethod
    def bar( cls, x ) :
        return x+1

    bar_vect = np.vectorize( bar )

Foo.bar_vect( np.array([ 1, 2, 3 ]) )

>> TypeError: 'classmethod' object is not callable

EDIT 1 :

'classmethod' object is not callable is a problem raising the same error, but with a lot of workarounds. My question is meant to go straight to the point and have a clear idea of how to use @classmethod without a scope giving access to cls.

Another try that I made was the following :

import numpy as np

class Foo( object ) :

    @classmethod
    def bar( cls, x ) :
        return x+1

    bar_vect = np.vectorize( bar )

>> NameError: name 'Foo' is not defined
Community
  • 1
  • 1
Thrastylon
  • 853
  • 7
  • 20
  • I can't understand the downvote. The question is legitimate and its redaction follows the guidelines. What is the point of downvoting this ? – Thrastylon Jun 01 '16 at 13:21
  • Possible duplicate of ['classmethod' object is not callable](http://stackoverflow.com/questions/11058686/classmethod-object-is-not-callable) (Note: Not down-voter, but searching your error message would have led you to a similar question instantly) – ShadowRanger Jun 01 '16 at 13:22
  • ['classmethod' object is not callable](http://stackoverflow.com/questions/11058686/classmethod-object-is-not-callable) doesn't get right to the point as my question do. – Thrastylon Jun 01 '16 at 13:23
  • True. It's the same fundamental problem, but a more indirect expression. Removing my close vote (if I can). – ShadowRanger Jun 01 '16 at 13:27

3 Answers3

3

@classmethods are implemented as a special object that gets processed using the descriptor protocol when looked up on the class; inside the definition, as a raw name (unqualified), it's a special classmethod object, not a normal function and it's not bound to the class properly. If you check the pure Python definition of classmethod, you'll note it's just a normal object that implements __init__ (for construction) and __get__ (for descriptor lookup), but not __call__, meaning that if you have the raw classmethod object, it's not actually a callable at all.

The trick is to qualify the reference so the "magic" happens to bind it to the class, and move the qualified reference outside the class definition (so Foo is a defined name and can be referenced for binding) changing:

class Foo(object):
    ... rest of class ...
    bar_vect = np.vectorize(bar)  # Indented and unqualified, BAD

to:

class Foo(object):
    ... rest of class ...
# Must qualify both bar_vect and bar, since no longer in class definition
Foo.bar_vect = np.vectorize(Foo.bar)  # Dedented, so Foo is defined for referencing, GOOD

Note that since you're using a classmethod, I suspect you may eventually be interested in subclassing and overriding bar. As written, you'd need to explicitly redefine bar_vect after defining each subclass, or it would use the inherited bar_vect, based on Foo.bar, even if the subclass defines its own bar classmethod. Explicitly redefining bar_vect each time is an option, but the other approach is to use metaclasses to implicitly define bar_vect when a class redefines bar:

class BarVectorized(type):
    def __new__(cls, name, bases, namespace, **kwargs):
        newcls = type.__new__(cls, name, bases, dict(namespace))
        # Make vectorized wrapper for this class (must use new wrapper
        # even if bar unchanged, so cls in bar is correct for lookup of
        # other class attributes/methods)
        try:
            newcls.bar_vect = np.vectorize(newcls.bar)
        except AttributeError:
            pass  # Allow class w/o bar; remove try/except if class must have bar
        return newcls

class Foo(object):
    __metaclass__ = BarVectorized
    @classmethod
    def bar(cls, x): return x + 1

class Foo2(Foo):
    ADD = 2  # Hardcoded 1 is dumb, use class attribute instead!
    @classmethod
    def bar(cls, x):
        return x + cls.ADD

class Foo3(Foo2):
    ADD = 3  # Provide new class attr to change Foo2.bar behavior when called via Foo3

>>> Foo.bar_vect([1,2,3])
array([2, 3, 4])
>>> Foo2.bar_vect([1,2,3])
array([3, 4, 5])
>>> Foo3.bar_vect([1,2,3])
array([4, 5, 6])

No need to define bar_vect explicitly at all, and bar_vect seamlessly uses the most local classes' definition of bar available at class definition time, so unless bar is redefined after class definition, it always works, and it works as efficiently as possible. To make it use bar live, you'd need to resort to more extreme measures that perform dynamic lookup and (barring a cache) reconstruction of the np.vectorize object on each use, which is suboptimal to say the least.

For completeness, a dynamic caching based solution (hat tip to Tadhg McDonald-Jensen's answer) that uses a dynamically populating cache that adds minimal overhead (and more importantly in my opinion, abstracts out the boilerplate code that's irrelevant to the work) for the case where the cache entry already exists by using a dict subclass defining __missing__:

import operator
import numpy as np

class ClassAttrRegistry(dict):
    '''Dictionary keyed by classes which returns optionally wrapped cached attributes'''
    __slots__ = '_wrapper', '_attrgetter'
    def __init__(self, attr, wrapperfunc=lambda x: x):
        self._wrapper = wrapperfunc
        self._attrgetter = operator.attrgetter(attr)
    def __missing__(self, cls):
        self[cls] = wrapped = self._wrapper(self._attrgetter(cls))
        return wrapped

class Foo(object):
    @classmethod
    def bar(cls, x):
        return x + 1

    # Dunder prefix makes cache private to Foo methods; if subclass overrides bar_vect,
    # assumed it's more complex than "vectorized bar"; cache should not be used
    __bar_vect_registry = ClassAttrRegistry('bar', np.vectorize)
    @classmethod
    def bar_vect(cls, x):
        # Get cached vectorized bar (creating if needed) then call it
        return cls.__bar_vect_registry[cls](x)

Subclasses don't need to (and should not) override bar_vect (and can't accidentally access __bar_vect_registry because it's name mangled such that only methods defined by Foo will see it; change name to _bar_vect_registry, one underscore, if it should be accessible to subclasses), they just override bar and Foo's bar_vect will create/cache vectorized accessors when bar_vect is first accessed on the subclass (or an instance thereof).

Community
  • 1
  • 1
ShadowRanger
  • 143,180
  • 12
  • 188
  • 271
  • I tried this already : `NameError: name 'Foo' is not defined` – Thrastylon Jun 01 '16 at 13:24
  • 1
    @Thrastylon: Ah, whoops. My mistake for testing in interactive interpreter with `Foo` already defined. You need to dedent the definition of `bar_vect` outside the class definition so `Foo` is defined first. I'll update. – ShadowRanger Jun 01 '16 at 13:28
  • Don't worry, I made **exactly** the same error doing my own interactive work to find a solution. – Thrastylon Jun 01 '16 at 13:30
  • This solution makes sense indeed. I am just amazed there is no solution to encapsulate it in the class definition so I can have all in the same place (the class definition scope). – Thrastylon Jun 01 '16 at 13:34
  • Works like a charm anyway. I'm glad to see these _outside-the-`class`-definition_ definitions are callable from `instance` methods defined inside the `class` definition. – Thrastylon Jun 01 '16 at 13:42
  • @Thrastylon: The problem is that Python classes are not self-referencable at definition time (no implicit equivalent to the explicit `self`/`cls` passed to methods & classmethods). The descriptor protocol is only invoked from qualified references (if it wasn't, you couldn't alias a `classmethod` to 2 names by doing `alias = origclsmeth` in the class definition; it would invoke the descriptor protocol & return a bound method, not a `classmethod` object). There are other reasons related to Python metaprogramming (partially defined class can't be referenced w/o causing problems for metaclass). – ShadowRanger Jun 01 '16 at 13:48
  • 1
    @Thrastylon: Yar, when defined outside the class and then assigned, it's a little weird (in a way that works for you). Because `numpy.vectorize` is a callable object, not a plain function, it doesn't try to convert it to an unbound method on `Foo` (that would be bound and have `self` passed on call). The downside is that it's not a `classmethod`, it's effectively a `staticmethod` that only wraps `Foo.bar`; even if a subclass defined a new `bar`, the subclasses inherited `bar_vect` would still wrap `Foo.bar`. You'd need metaclasses or (cached) properties to work around that. – ShadowRanger Jun 01 '16 at 13:52
  • One of the only reasons to use `classmethod` in my opinion is to allow subclasses to define attributes / other methods that will be used in the classmethod, this absolutely breaks that functionality by using the callable that is bound to the `Foo` class ([my answer](http://stackoverflow.com/a/37570908/5827215) has the solution to this) – Tadhg McDonald-Jensen Jun 01 '16 at 14:09
  • @TadhgMcDonald-Jensen: I agree, that's why I was updating my answer to rectify the problem via metaclasses, to get definition time correct wrapping for subclasses w/o compromising performance. Your use of a `@classmethod` wrapper for `bar_vect` means that you pay a per-call cost of an extra layer of function wrapping and `vectorize` object reconstruction (or cache check) though `bar` likely never changes; if `bar` can change, that's worth it, but typically, it's poor form to monkey-patch classes after definition time, and if you need to do it, you can explicitly regenerate `bar_vect` anyway). – ShadowRanger Jun 01 '16 at 14:26
  • did you see my solution? I was considering metaclass but I like to avoid them when possible. – Tadhg McDonald-Jensen Jun 01 '16 at 14:29
  • @ShadowRanger what if `bar` needs to use methods/attributes defined in subclasses? if you did `def bar(cls,x):return x + cls.a` and overridden `a` in a subclass it still wouldn't work... You need to use a separate `np.vectorize` for each class, not just the ones that override `bar` – Tadhg McDonald-Jensen Jun 01 '16 at 16:41
  • @TadhgMcDonald-Jensen: Good point. Removed conditional in favor of unconditional rewrapping (which is actually simpler, code-wise, and only trivially higher overhead, paid only at definition time, not use time). Tweaked example to demonstrate new behavior. – ShadowRanger Jun 01 '16 at 17:05
2

Your confusion to why this is not an easy work around is understandable, let me elaborate on to why using classmethod in this way isn't going to work...

The way classmethod works is that it creates a descriptor, an object that implements __get__ when it is retrieved as an attribute on an object.

So when you do Foo.bar it basically loads the bar classmethod and calls:

bar.__get__(None, Foo)

Where the None represents the instance (there is None because it is on the class itself) and the second argument represents the class, a classmethod is not callable because then it would not have a class to bind it too!

Not only this but the class object to bind it too doesn't exist until the class definition block has ended (and the metaclass type actually puts it together) so the bare minimum is to create bar_vect after the class is actually defined:

class Foo( object ):
    a = 1 #lets use an example that actually uses the class
    @classmethod
    def bar( cls, x ):
        return x+cls.a

Foo.bar_vect = np.vectorize( Foo.bar )

This will work sure, but then you break the functionality of subclasses, what if you wanted to change a?

class Subfoo(Foo):
    a = 3 #this will have no effect on 

assert Subfoo.bar_vect(np.array([ 1, 2, 3 ])) == np.array([ 4, 5, 6 ])
#this SHOULD work but doesn't because you bound bar_Vect to just Foo
#subclasses mean nothing to your class method

The only way to make it work in this case is to recreate the np.vectorize at least one for each subclass, the simplest version is to just do it every time you call bar_vect:

class Foo( object ):
    a = 1
    @classmethod
    def bar( cls, x ):
        return x+cls.a
    @classmethod
    def bar_vect(cls,arg):
        return np.vectorize(cls.bar)(arg)

This is obviously undesirable because it calls np.vectorize every time x.bar_vect is used, however you could make a record of all the classes and only make it when a new class is used:

_bar_vect_registry = {}
@classmethod
def bar_vect(cls,arg):
    try:
        return cls._bar_vect_registry[cls](arg)
    except KeyError:
        cls._bar_vect_registry[cls] = np.vectorize(cls.bar)
        return cls._bar_vect_registry[cls](arg)
Thrastylon
  • 853
  • 7
  • 20
Tadhg McDonald-Jensen
  • 20,699
  • 5
  • 35
  • 59
  • The registry idea is good, thank's for sharing. I had no use for `subclasses` in my particular case, but this is definitely an improvement for a more scalable architecture. – Thrastylon Jun 01 '16 at 16:40
  • 1
    do you need the `classmethod` at all? you could just do `@np.vectorize; def bar_vect(x):....` if that is the final result you are after. – Tadhg McDonald-Jensen Jun 01 '16 at 17:00
  • 1
    if you are using the `class` block to define a group of functions (everything is a `classmethod` and you have no intention on subclasses or instances) then I'd recommend you use a module instead of a class as that is what modules are intended for. – Tadhg McDonald-Jensen Jun 01 '16 at 17:09
  • @Thrastylon, @Tadhg: Yeah, `@classmethod` is only useful if you need to handle subclasses (or need to use class attributes and would prefer to avoid repeating the class name over and over); otherwise, you'd use `@staticmethod`, or, as Tadhg points out, for a callable object type like `np.vectorize`, you can use clever tricks using `np.vectorize` as a decorator (which has the implicit effect of making the decorated method act like a `staticmethod` thanks to `np.vectorize` not being a function, but rather a more general callable object that lacks binding behaviors). – ShadowRanger Jun 01 '16 at 17:11
  • @TadhgMcDonald-Jensen: It's not completely unreasonable to use non-instantiated classes for namespacing, but yeah, if the number of namespaces and entries in each namespace is high, it's better for code organization and efficiency to use separate sub-modules/sub-packages in a higher level package, rather than a bunch of classes in a module. Separate modules mean you can avoid loading unused code, but it can introduce annoying cross-module dependencies (possibly cyclic) if the namespaces reference each other; if they all cross-reference, then you don't avoid loading dead code anyway. – ShadowRanger Jun 01 '16 at 17:15
  • @TadhgMcDonald-Jensen: I made a slight variant on your cache/registry approach in my answer (would have been comment, but needed newlines). Only real difference is that it makes the cache private to `Foo` (subclasses can't accidentally modify/access/stomp cache), and uses a custom `dict` subclass so `bar_vect` itself doesn't require as much boilerplate (maintaining performance w/`__missing__` based `dict` subclass as fast as a plain `dict` when cache entry exists). Not trying to step on toes; for completeness. Let me know what you think. Up-voted this answer to "give credit". – ShadowRanger Jun 01 '16 at 18:00
  • I like it, although if you are going that far why not make **it** the descriptor? just adding `def __get__(self,inst,cls):return self[cls]` and use `bar_vect = ClassAttrRegistry(...)`, then just `Foo.bar_vect` would give you the specific `np.vectorize` object for `Foo` instead of a wrapper for it. then you are not locked out of things like `Foo.bar_vect.excluded` or calling with additional arguments or documentation. (P.S. at this point we aren't even discussing the original question lol) – Tadhg McDonald-Jensen Jun 01 '16 at 18:25
1

You real problem is that you try to use bar before the class if fully constructed, so you do not get the expected object.

Here is a simplified example:

class Foo:
    @classmethod
    def bar(cls, x):
        print ('bar called in', cls, 'with', x)
    barv = str(bar)

print(str(Foo.bar))
print(Foo.barv)

gives:

<bound method Foo.bar of <class '__main__.Foo'>>
<classmethod object at 0x00000000035B0320>

That shows that until the class is fully constructed, the methods identifier are only bound to the method definitions and not to the real methods.

If you want achieve what you want, you must define the class variable outside of class definition (after last line), as explained by @ShadowRanger

Serge Ballesta
  • 143,923
  • 11
  • 122
  • 252