1

I have a decorator method to check the argument types passed to a function.

def accepts(*types):
    def check_accepts(f):
        assert len(types) == f.func_code.co_argcount
        def new_f(*args, **kwds):
            for (a, t) in zip(args, types):
                assert isinstance(a, t), \
                       "arg %r does not match %s" % (a,t)
            return f(*args, **kwds)
        new_f.func_name = f.func_name
        return new_f
    return check_accepts

Now, in a class (in classA.py), I want a method to only accept arguments of the same class:

class ClassA:
    @accepts(WHATTOPUTHERE)
    def doSomething(otherObject):
        # Do something

In other classes I can just put ClassA in place of WHATTOPUTHERE, but inside classA.py, ClassA is not known. How can I pass the current class to the @accepts() function?

GroovyPanda
  • 3,694
  • 8
  • 31
  • 42
  • Sorry, I don't know what you mean by that. That is not my intention, no. Can you explain why that would happen? – GroovyPanda Jan 14 '14 at 16:08
  • Hrm, come to think of it, that won't happen here; you've reimplemented the same type check that methods *already* do in Python 2; force `self` to be of the same type or a subclass of the current class. But it does appear as if you didn't take into account that `args[0]` is `self` here. – Martijn Pieters Jan 14 '14 at 16:13

2 Answers2

1

Use the function based version of the decorator and apply it after the class definition:

class ClassA:
    def doSomething(otherObject):
        # Do something
ClassA.doSomething = accepts(ClassA)(ClassA.doSomething)

Another way would be write a Metaclass that would automatically apply this after class creation:

class Meta(type):

    def __new__(cls, clsname, bases, dct):
       fields = ('doSomething', ) #Fields on which you want to apply the decorator
       for name, val in dct.items():
           if name in fields:
               dct[name] = accepts(cls)(val)
       return type.__new__(cls, clsname, bases, dct)


class ClassA(object):
    __metaclass__ = Meta
    def doSomething(otherObject):
        pass

Instead of manually doing things like new_f.func_name = f.func_name, use functools.wraps. This would also preserve things like docstring, argument list etc.

from functools import wraps
def accepts(*types):
    def check_accepts(f):
        print "inside"
        assert len(types) == f.func_code.co_argcount
        @wraps(f)
        def new_f(*args, **kwds):
Community
  • 1
  • 1
Ashwini Chaudhary
  • 244,495
  • 58
  • 464
  • 504
0

wouldn't adding a self variable to the function doSomething and then referencing args[0] inside check_accepts (provided you add (*args) or extra argument to your definition) solve your problem? If the function doSomething is supposed to be a class method, still you can outsource this self. How?

  • add a self to some dummy method inside the class
  • make a decorator which populates a variable which accepts can somehow reach (like metadata)
  • make sure to call this additional method before the doSomething()
  • you've got the class instance! Enjoy!

NOTE: This is not the only way to store metadata like this and use it later, you can do it as you wish.

Ishan Srivastava
  • 1,129
  • 1
  • 11
  • 29