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'