Obtaining a callable class method object from a specific class

Discussion in 'Python' started by Nathan Duran, Apr 10, 2008.

  1. Nathan Duran

    Nathan Duran Guest

    This is a contrived pseudocode example which has been broken out of a
    larger problem, so it may seem like a strange thing to want to do,
    but...

    I have a group of objects which inherit (single) from a common base
    class like so:

    ---
    class Root(object):
    @classmethod
    def CumulativeScore(cls, arg):
    #Ask every child class to
    #generate a score and add
    #them together
    cumulativescore = 0
    for base in cls.mro():
    cumulativescore += base.Score(arg)
    return cumulativescore
    #No Score method defined in Root so don't try to call one!

    class BranchChild(Root):
    @classmethod
    def Score(cls, arg):
    return 1

    class LeafChild(BranchChild):
    @classmethod
    def Score(cls, arg):
    return 3

    class LeafChild2(BranchChild):
    pass
    #No Score method defined here, either!

    ---

    The goal is to be able to call CumulativeScore(arg) on an instance of
    any of these objects (Root, Branch or Leaf) which will then chain
    calls (top down) to each subclass' Score method if (and only if) one
    is defined, returning the sum of all of these calls. Kinda like
    constructor chaining, only I don't want it to be explicit/cooperative
    because super() doesn't seem to work in classmethods and I want to
    reduce the amount of redundant boilerplate code in the subclasses
    (which will be numerous).

    In order to do this I really need a way to ask LeafChild to give me
    *its* Score method *if* it has one of its own. I don't want its
    parent's method or its grandparent's method (until I get to them of
    course), just the one that's (optionally) defined in LeafChild, so
    getattr() and __dict__ are of no use to me. The only thing I've been
    able to find that actually works is inspect.classify_class_attrs().
    While it delivers the expected behavior, classify_class_attrs spews
    out a ton of superfluous information which I have to parse myself, and
    the method objects it returns in its tuples are not callable to boot.
    This leads to ugly looking code like this:

    ---
    @classmethod
    def CumulativeScore(cls, arg):
    cumulativescore = 0
    mro = list(cls.mro())
    mro.reverse()
    for base in mro:
    matchfunc = [getattr(base, "Score") for attr in
    inspect.classify_class_attrs(base) if attr[0] == "Score" and attr[2]
    == base]
    if len(matchfunc) == 1:
    cumulativescore += matchfunc[0](arg)
    return cumulativescore
    ---

    In looking through the inspect module's documentation, it seems as
    though getmembers() once offered the functionality I require, but no
    longer:

    "Changed in version 2.2: im_class used to refer to the class that
    defined the method."

    I've gotten the feeling from the Python documentation that
    classmethods are seen as third-class citizens, but they are
    unfortunately perfect for my needs, and it doesn't seem like this
    should be as complicated as it is. Is there a simpler, more elegant
    way to ask a class object if it has a particular method definition
    that I've missed somewhere? If not, why can't classify_class_attrs at
    least return a callable method object for me (yes, I've read the
    "unifying" paper)?

    Thanks!
    Nathan Duran, Apr 10, 2008
    #1
    1. Advertising

  2. On Apr 10, 7:47 pm, Nathan Duran <> wrote:
    > This is a contrived pseudocode example which has been broken out of a  
    > larger problem, so it may seem like a strange thing to want to do,  
    > but...
    >
    > I have a group of objects which inherit (single) from a common base  
    > class like so:
    >
    > ---
    > class Root(object):
    >      @classmethod
    >      def CumulativeScore(cls, arg):
    >          #Ask every child class to
    >          #generate a score and add
    >          #them together
    >          cumulativescore = 0
    >          for base in cls.mro():
    >              cumulativescore += base.Score(arg)
    >          return cumulativescore
    >      #No Score method defined in Root so don't try to call one!
    >
    > class BranchChild(Root):
    >      @classmethod
    >      def Score(cls, arg):
    >          return 1
    >
    > class LeafChild(BranchChild):
    >      @classmethod
    >      def Score(cls, arg):
    >          return 3
    >
    > class LeafChild2(BranchChild):
    >      pass
    >      #No Score method defined here, either!
    >


    I won't question why you want to do this...
    Here is a solution base on a metaclass, but it feels wrong.

    @classmethod
    def score(cls, args):
    return 0

    def totalscore(rootcls, arg):
    return sum(cls.score(arg) for cls in rootcls.mro()
    if hasattr(cls, 'score'))

    class MetaScore(type):
    def __new__(meta, name, bases, attrs):
    attrs.setdefault('score', score)
    return type.__new__(meta, name, bases, attrs)

    class Root(object):
    __metaclass__ = MetaScore

    class Branch(Root):
    @classmethod
    def score(cls, arg):
    return 1

    class Leaf(Branch):
    @classmethod
    def score(cls, arg):
    return 3

    class Leaf2(Branch):
    pass

    --------- Test ---------

    >>> totalscore(Root, 1)

    0
    >>> totalscore(Branch, 1)

    1
    >>> totalscore(Leaf, 1)

    4
    >>> totalscore(Leaf2, 1)

    1

    --
    Arnaud
    Arnaud Delobelle, Apr 10, 2008
    #2
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Edward Diener

    Static method object not callable

    Edward Diener, Aug 11, 2004, in forum: Python
    Replies:
    8
    Views:
    681
    gblandst
    Mar 27, 2008
  2. exiquio
    Replies:
    2
    Views:
    520
    exiquio
    Oct 7, 2008
  3. netimen
    Replies:
    8
    Views:
    290
    netimen
    Oct 23, 2008
  4. Kamarulnizam Rahim
    Replies:
    5
    Views:
    120
    Kamarulnizam Rahim
    Feb 2, 2011
  5. Ulrich Eckhardt
    Replies:
    6
    Views:
    94
    Peter Otten
    Jul 12, 2013
Loading...

Share This Page