theoemms
theoemms

Reputation: 68

Allowing a python decorator to take a class as a parameter when used inside the class being passed

I'm having some difficulty with Python decorators, and I think it has to do with the fact I am passing a class as a parameter to a function decorator, when the function being decorated is a method of the class being passed.

I have no clearer explanation of the problem than that, so hopefully some code will help:

from typeChecking import *

class Vector:
    @accepts(Vector, float, float, float) #Vector hasn't been defined yet... In c++ I could forward declare...
    def __init__(self, x, y, z):
        self._x = float(x)
        self._y = float(y)
        self._z = float(z)
    ...

I don't think the definition of @accepts is important, but I'll leave it here just in case:

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

I get the following error:

File "raytracerBase.py", line 41, in <module>
  class Vector:
File "raytracerBase.py", line 42, in Vector
  @accepts(Vector, float, float, float)
NameError: name 'Vector' is not defined

I think my comment explains what I think is happening: The class hasn't been defined yet (because, I'm in the process of defining it) and therefore I can't pass it.

Is there a neat workaround that isn't:

assert isinstance(self, Vector), "Something is wrong... very wrong..."

I am aware I'm type checking more than is deemed necessary, but I would like to know how to solve this sort of problem anyway.

Edit: I'm also aware that @accepts isn't actually valid Python. But, It's the outline of the code I intend to implement.

Upvotes: 4

Views: 237

Answers (1)

schesis
schesis

Reputation: 59168

The short answer is No, you can't refer to your class like that before you've finished defining it.

Type-checking is a subject under active discussion among the Python development team at the moment, and to the extent that there's a consensus, it's that function annotations are the way forward: PEP 484 describes the direction Guido wants to take in implementing it.

The workaround for forward references proposed in that PEP is to just use a string instead:

When a type hint contains names that have not been defined yet, that definition may be expressed as a string, to be resolved later. For example, instead of writing:

def notify_by_email(employees: Set[Employee]): ...

one might write:

def notify_by_email(employees: 'Set[Employee]'): ...

Upvotes: 5

Related Questions