0

Problem

Unable to dynamically create classes with local scope within a function but works at the top level.

Question

How to create classes dynamically at the correct namespace scope.


I'm trying to dynamically create classes that are added to the callers namespace.

So that I'm able to do the following.

import creator
creator.make('SomeClass')
print "SomeClass :", SomeClass

This can be made to work when adding dynamic classes at the top level but when trying to do the same within a function, so that the created class has only local scope, it doesn't work.

I saw that there is hack which works when placed inside the function but that's going to disappear.

The following code shows the problem obviously there would be more to creator.py than that shown.

# creator.py    
import inspect

def make(classname):
    t = type(classname, (object,), { })
    frame = inspect.currentframe()
    print "WILL make() - callers locals :", frame.f_back.f_locals.keys()
    frame.f_back.f_locals[classname] = t
    print "DID  make() - callers locals :", frame.f_back.f_locals.keys()
    return t

# example.py
import creator

print  "WILL __main__ - locals :", locals().keys()
creator.make('SomeClass')
print  "DID  __main__ - locals :", locals().keys()

print "SomeClass :", SomeClass
print "-" * 80

def closed():
    # This hack helps
    # https://stackoverflow.com/a/1549221/1481060
    # exec ''

    class ClosedClass: pass

    print  "WILL closed() - locals :", locals().keys()
    creator.make('AnotherClass')
    print  "DID  closed() - locals :", locals().keys()

    # NOT EXPECTED
    try: print  AnotherClass
    except NameError, ex: print "OUCH:", ex


closed()

Output:

WILL __main__ - locals : ['creator', '__builtins__', '__file__', '__package__', '__name__', '__doc__']
WILL make() - callers locals : ['creator', '__builtins__', '__file__', '__package__', '__name__', '__doc__']
DID  make() - callers locals : ['creator', '__builtins__', 'SomeClass', '__file__', '__package__', '__name__', '__doc__']
DID  __main__ - locals : ['creator', '__builtins__', 'SomeClass', '__file__', '__package__', '__name__', '__doc__']
SomeClass : <class 'creator.SomeClass'>
--------------------------------------------------------------------------------
WILL closed() - locals : ['ClosedClass']
WILL make() - callers locals : ['ClosedClass']
DID  make() - callers locals : ['ClosedClass', 'AnotherClass']
DID  closed() - locals : ['ClosedClass', 'AnotherClass']
OUCH: global name 'AnotherClass' is not defined

Of course I don't want OUCH:...

Looking at locals() within closed one would imagine it should work but as mentioned in the hack the Python compiler is optimising the locals and missing the introduction of a new dynamic one.

If I uncomment the exec '' line then it works.

I could create theses classes with global scope but I'm trying to keep my namespaces clean.

I could of course do AnotherClass = creator.make('AnotherClass') but I'm trying to keep it DRY.

Is there a way of making it work without the exec '' hack ?

Or is it a case of making them global?

1 Answer 1

2

No. The only way to access local variables is via locals() or vars() and in both cases the documentation specifically tells that there is no guarantee that modifications to the dict return will affect the local variables, hence: there is no way to do this reliably.

If you want modify the locals statically then maybe you can modify the code object associated with the function to provide more locals, but it may not be possible or it could be really hard to do reliably, and I see no reason for doing this.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.