【问题标题】:Best approach with dynamic classes using Python globals()使用 Python globals() 动态类的最佳方法
【发布时间】:2008-10-21 14:41:54
【问题描述】:

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

从全局命名空间中动态拉取类是可行的:

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

而且肯定比以下更简洁:

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

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

【问题讨论】:

    标签: python coding-style namespaces


    【解决方案1】:

    这种方法的一个缺陷是它可能使用户能够做的比你希望的更多。他们可以通过提供名称来调用该命名空间中的 any 单参数函数。您可以通过一些检查来帮助防止这种情况(例如。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. 从子类树构造一个查找字典。让你的所有类都继承自一个基类。创建所有类后,检查所有基类并从中填充字典。这样做的好处是您可以在任何地方定义您的类(例如,在单独的模块中),并且只要您在所有创建注册表后创建注册表,您就会找到它们。

      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)
      

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

    【讨论】:

    • 作为2008年的问答我想再问一下?使用 globals() 仍然是一个安全问题吗?
    【解决方案2】:

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

    确实,您的执行方式没有错误,但根据我的经验,它往往表明您的程序中存在某种“缺少抽象”。您基本上是依靠 Python 解释器来存储您可能需要的对象列表,而不是自己存储。

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

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

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

    安全问题对于网络应用来说尤为突出。在 variable 是从 Web 表单输入的情况下执行 globals()[variable] 只是自找麻烦

    【讨论】:

      【解决方案3】:

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

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

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2023-03-08
        • 1970-01-01
        • 2021-10-20
        • 1970-01-01
        • 2018-07-22
        相关资源
        最近更新 更多