繁体   English   中英

使用Python全局变量的动态类的最佳方法()

[英]Best approach with dynamic classes using Python globals()

我正在开发一个Web应用程序,它将根据用户输入返回一组可变模块。 每个模块都是一个Python类,其构造函数接受单个参数,并具有包含输出的“.html”属性。

从全局命名空间动态提取类的工作原理如下:

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

而且它肯定比以下更简洁:

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

什么被认为是风格上写这个的最佳方式? 是否存在不使用全局命名空间的风险或原因?

这种方法的一个缺陷是它可能使用户能够比你想要的更多。 他们可以通过提供名称来调用该命名空间中的任何单参数函数。 您可以通过一些检查来帮助防范这种情况(例如isinstance(SomeBaseClass,theClass),但它可能更好地避免这种方法。另一个缺点是它限制了您的类位置。如果您最终得到了几十个这样的类并决定要将它们分组到模块中,您的查找代码将停止工作。

您有几种备选方案:

  1. 创建显式映射:

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

    虽然这样做的缺点是你必须重新列出所有的类。

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

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

    你在这里无意中暴露了一些额外的类变量(__bases __,__ getattribute__等) - 可能无法利用,但并不完美。

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

     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) 

    上面的一个更高级的变体是使用自注册类 - 创建一个元类,而不是自动在dict中注册任何创建的类。 对于这种情况,这可能有点过头了 - 但它在一些“用户插件”场景中很有用。

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

你这样做的方式没有任何问题 ,实际上,但根据我的经验,它往往表明你的程序中存在某种“缺失的抽象”。 您基本上依赖Python解释器来存储您可能需要的对象列表,而不是自己存储它。

因此,作为第一步,您可能只想创建一个您可能想要调用的所有类的字典:

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

最初,这不会产生太大的影响。 但随着您的Web应用程序的增长,您可能会发现以下几个优点:(a)您不会遇到命名空间冲突,(b)使用globals()您可能遇到安全问题,攻击者实际上可以访问任何全局符号你的程序,如果他们可以找到一种方法将任意classname注入你的程序,(c)如果你想让classname不是实际的确切类名,使用你自己的字典会更灵活,(d)你可以使用更灵活的用户定义类来替换dispatch字典,该类可以在需要时进行数据库访问或类似的操作。

对于Web应用程序,安全问题特别突出。 执行globals()[variable]从web表单输入variable只是在寻找麻烦

在类名和类之间构建映射的另一种方法:

定义类时,将属性添加到要放入查找表的任何类中,例如:

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")])

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM