2008-10-21 80 views
6

我正在研究一个Web应用程序,它将根据用户输入返回一组变量模块。每个模块都是一个带有构造函数的Python类,它接受单个参数并且具有包含输出的“.html”属性。使用Python全局变量动态类的最佳方法()

从全局命名空间动态拉动类的工作:

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

而且它比肯定更简洁:

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

什么被认为是写这篇文章,文体最好的方法是什么?是否有风险或理由不使用全局命名空间?

回答

6

这种方法的一个缺陷是,它可能会让用户能够超过你想要的。他们只需提供名称即可在该名称空间中调用任意单参数函数。你可以用一些检查来帮助防止这种情况(例如,isinstance(SomeBaseClass,theClass)),但它可能更好地避免这种方法。另一个缺点是它限制了你的班级安排。如果你最终得到几十个这样的班级并决定他们组成模块,您的查找代码将停止工作

您有几种备选方案:

  1. 创建一个明确的映射:

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

    虽然这样也有,你有缺点重新列表al l课程。

  2. 将这些类嵌套在封闭范围内。例如。自己的模块中定义它们,或外部类中:

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

    你在不经意间暴露了几个额外类变量的这里虽然(__bases__来得到,__getattribute__等) - 可能无法利用,但并不完美。

  3. 从子类树构造一个查找字典。让所有的类都从一个基类继承而来。当所有的类都被创建后,检查所有的基类并从它们中填充一个字典。这样做的好处是你可以在任何地方定义你的类(例如,在单独的模块中),并且只要你在创建完成之后创建注册表,你就可以找到它们。

    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) 
    

    以上更高级的变体是使用自注册类 - 创建一个元类比在一个字典自动注册任何创建的类。对于这种情况,这可能是过分的 - 它在一些“用户插件”场景中很有用。

4

首先,这听起来像你可能重新发明了一点点......大多数Python Web框架(CherryPy/TurboGears就是我所知道的)已经包含一种方法来根据内容向特定类发送请求的URL或用户输入。

没有什么错误与你这样做的方式,真的,但根据我的经验,它往往表明你的程序中的某种“缺少抽象”。你基本上依靠Python解释器来存储你可能需要的对象列表,而不是自己存储它。

因此,作为第一步,你可能只想让所有你可能需要调用类的词典:

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

最初,这不会有太大的差别。但随着您的网络应用程序的增长,您可能会发现几个优点:(a)您不会遇到名称空间冲突,(b)使用globals()您可能存在安全问题,攻击者本质上可以访问程序中的任何全局符号(c)如果你想让classname不是实际的确切类名,那么使用你自己的字典将会更加灵活,(d)你可以替换dispatch字典与一个更灵活的用户定义的类,可以访问数据库或类似的东西,如果你发现需要。

Web应用程序的安全问题尤其突出。做globals()[variable]其中variable是从网络表单输入只是问题

0

另一种方式来建立类名和类之间的映射:

当定义类,添加要放在查找表中任一类别的属性,如:

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

完成此操作后,构建查找图为:

class_lookup = zip([(c, globals()[c]) for c in dir() if hasattr(globals()[c], "lookup")]) 
相关问题