Best approach with dynamic classes using Python globals ()

I am working on a web application that will return a variable set of modules depending on user input. Each module is a Python class with a constructor that takes a single parameter and has an .html property containing the output.

The work of a dynamic class with a global namespace works:

result = globals()[classname](param).html 

And this, of course, is more concise than:

 if classname == 'Foo': result = Foo(param).html elif classname == 'Bar': ... 

What is considered the best way to write this, stylistically? Are there any risks or reasons for not using the global namespace?

+6
python coding-style namespaces
source share
3 answers

The disadvantage of this approach is that it can give the user more than you want. They can call any one-parameter function in this namespace, simply by specifying a name. You can help prevent this with a few checks (for example, isinstance (SomeBaseClass, theClass), but it is probably best to avoid this approach. Another disadvantage is that it limits the placement of your class. If you end up with dozens of such classes and decide to group them into modules, your search code will stop working.

You have several alternatives:

  • Create an explicit mapping:

      class_lookup = {'Class1' : Class1, ... } ... result = class_lookup[className](param).html 

    although this has the disadvantage that you need to list all classes.

  • Nested classes in the scope. For example. define them in your own module or in an outer class:

     class Namespace(object): class Class1(object): ... class Class2(object): ... ... result = getattr(Namespace, className)(param).html 

    You do unintentionally expose here a couple of additional class variables (__bases__, __getattribute__, etc.) - probably not usable, but not ideal.

  • Build a lookup dict from a subclass tree. Make all your classes inherited from the same base layer. When all classes are created, study all base classes and fill them with a dict. This has the advantage that you can define your classes anywhere (for example, in separate modules), and as long as you create the registry after creating all, you will find them.

     def register_subclasses(base): d={} for cls in base.__subclasses__(): d[cls.__name__] = cls d.update(register_subclasses(cls)) return d class_lookup = register_subclasses(MyBaseClass) 

    A more complex variation above is to use self-registering classes - to create a metaclass than to automatically register any created classes in a dict. This is probably too large for this case - it is useful in some "custom plugin" scenarios.

+6
source share

First of all, it looks like you can reinvent the wheel a bit ... most Python web frameworks (CherryPy / TurboGears is what I know) already include a way to send requests to specific classes based on URL content or user input.

There is nothing wrong with the way you do this, but, in my experience, it tends to indicate some kind of โ€œmissing abstractionโ€ in your program. You mostly rely on the Python interpreter to store a list of objects that you might need, and not to store it.

So, as a first step, you might just need to make a dictionary of all the classes you want to call:

 dispatch = {'Foo': Foo, 'Bar': Bar, 'Bizbaz': Bizbaz} 

Initially, this will not make much difference. But as your web application grows, you can find several advantages: (a) you will not encounter namespace conflicts, (b) using globals() , security issues can arise where an attacker can essentially gain access to any a global character in your program, if they can find a way to insert an arbitrary classname into your program, (c) if you ever want to have a classname as something other than a real exact class name, using your own dictionary will be more flexible (d) you can deputy it Dictionary dispatch more flexible user class that has access to a database or something like that, if you find it.

Security issues are especially important for a web application. Doing globals()[variable] , where variable is entered from a web form, just asks for problems .

+4
source share

Another way to build a map between class names and classes:

When defining classes, add an attribute to any class that you want to put in the lookup table, for example:

 class Foo: lookup = True def __init__(self, params): # and so on 

Once this is done, building a search map:

 class_lookup = zip([(c, globals()[c]) for c in dir() if hasattr(globals()[c], "lookup")]) 
0
source share

All Articles