Forward references are sometimes necessary and in interpreted languages like Python almost never a problem. In class definitions however, it is. In this article we examine a solution for this problem with the use of the built in globals()
function.
Consider the following use case: we have two classes A
and
B
and we want the __init__()
method of both classes to check that any arguments passed to it are instances of
the other class, i.e. the A()
constructor will only accept B
instances as argument
while the B()
constructor only accepts A
instances.
This isn't a problem per se because Python is an interpreted language. Is this context
that means that a reference to any class name in for example the __init__()
method
will be resolved at run time. The __init__()
method of the first class to be defined
can therefor use an expression like issubclass(argument.__class__,B)
without a
problem, even if B
is not yet defined.
But what if we want to parameterize this approach? Say we want to store the class that the arguments have to check against in a class variable. Such an assignment is executed while the class is being defined and therefor a forward reference (a reference to a class that is defined later in the file) will raise an exception. The following snippet of code illustrates the problem.
class A: argumentclass = B def __init__(self,*args): for a in args: if not issubclass(a.__class__,self.argumentclass): raise TypeError('argument not a subclass of' , self.argumentclass,a) self.refs = args
The code in line 2 will raise a NameError
exception.
There is a solution however: if we store the name of the class we can easily check if
an argument is a sub class of a class with that stored name if we have a means of
retrieving a class object by name. Fortunately that is quite simple by way of the
globals()
function. This built in function will return a dictionary of all objects in
the current globals scope. By the time the __init__()
method is executed this will hold
both the A
and the B
class. The adapted code may look something like this:
class A: argumentclass = 'B' def __init__(self,*args): for a in args: if not issubclass(a.__class__,globals()[self.argumentclass]): raise TypeError('argument not a subclass of' ,self.argumentclass,a) self.refs = args
This approach even allows for easy subclassing:
class X: argumentclass = None def __init__(self,*args): for a in args: if not issubclass(a.__class__,globals()[self.argumentclass]): raise TypeError('argument not a subclass of' ,self.argumentclass,a) self.refs = args class A(X): argumentclass = 'B' class B(X): argumentclass = 'A'