Reputation: 85
While trying to use introspection to navigate from strings to classes via some of the suggestions in Convert string to Python class object? I noticed that the given approaches won't work to get at a class in scope local to a function. Consider the following code:
import sys
def f():
class LocalClass:
pass
print LocalClass
print 'LocalClass' in dir(sys.modules[__name__])
which gives output
__main__.LocalClass
False
I'm a bit confused as to why LocalClass seems to belong to the main module according to the class object itself, and yet not accessible through sys.modules. Can someone give an explanation?
And is there a way to generate a class from a string, even if that class is only in non-global scope?
Upvotes: 0
Views: 73
Reputation: 3650
In the function f
, LocalClass
is indeed local. You can see this by trying __main__.LocalClass
and seeing that AttributeError: 'module' object has no attribute 'LocalClass'
is raised.
As to why the class returns __main__.LocalClass
is because by default, the __repr__
function returns <cls.__module__>.<cls.__name__>
.
The reason why dir
isn't finding it is because it only looks at the variables defined in its scope. LocalClass
is local so it won't show up if you are looking in the main module.
A way to create a class from a string can be done in many ways.
The first and easiest to understand is by using exec
. Now you shouldn't just go around using exec
for random things so I wouldn't reccomend using this method.
The second method is by using the type
function. The help page for it returns type(name, bases, dict)
. This means you can create a class called LocalClass
subclassed by object
with the attribute foo
set to "bar" by doing type("LocalClass", (object,), {"foo": "bar"})
and catching the returned class in a variable. You can make the class global by doing globals()["LocalClass"] = ...
PS: An easier (not sure if prettier) way to get the main module is by doing import __main__
. This can be used in any module but I would generally advise against using this unless you know what you are doing because in general, python people don't like you doing this sort of thing.
EDIT: after looking at the linked question, you dont want to dynamically create a new class but to retrieve a variable given it's name. All the answers in the linked question will do that. I'll leave you up to deciding which one you prefer the most
EDIT2: LocalClass.__module__
is the same as __main__
because that was the module you defined the class. If you had defined it in module Foo
that was imported by __main__
(and not actually ran standalone), you would find that __module__
would be "B". Even though LocalClass
was defined in __main__
, it won't automatically go into the global table just because it is a class - in python, as you might have already known, (almost) EVERYTHING is an object. The dir
function searches for all variables defined in a scope. As you are looking in the main scope, it is nearly equivalent to be doing __dict__
or globals()
but with some slight differences. Because LocalClass
is local, it isn't defined in the global context. If however you did locals()
whilst inside the function f
, you would find that LocalClass
would appear in that list
Upvotes: 2