【问题标题】:How to check in python if some class (by string name) exists?如果存在某个类(按字符串名称),如何检查python?
【发布时间】:2015-08-09 13:34:14
【问题描述】:

是否可以检查某个类是否存在?我在 json 配置文件中有类名。 我知道我可以简单地尝试通过类名字符串创建一个对象,但这实际上是一个坏主意,因为类构造函数可以做一些意想不到的事情,而此时我只想检查我的配置是否有效并且都提到了可以上课。

有什么办法吗?

编辑:我也明白你可以从某个模块中获取所有方法,在我的情况下,我不确定并且实际上并不关心该方法来自哪个模块。它可以来自任何 import 语句,我可能不知道具体来自哪里。

【问题讨论】:

  • 你的意思是一个类是否被正确导入并在命名空间中可用,或者是否创建了一个具有特定名称的类的实例?
  • 如果您已经导入了所有可用的对象,您可以简单地检查一下。你可以写一个try except NameError 块。如需更多信息,我们需要您的努力。
  • 你可以试试if locals().get('ClassName', False): ...
  • 这不是工作的一半吗?如果类存在,它是如何实例化的?

标签: python class dynamic


【解决方案1】:

你可以解析源码得到所有的类名:

from ast import ClassDef, parse
import importlib
import inspect

mod = "test"
mod = importlib.import_module(mod)
p = parse(inspect.getsource(mod))
names = [kls.name for kls in p.body if isinstance(kls, ClassDef)]

输入:

class Foo(object):
   pass

class Bar(object):
    pass

输出:

['Foo', 'Bar']

只需将配置中的类名与返回的名称进行比较。

{set of names in config}.difference(names)

如果您想包含导入的名称,您可以解析导入它的模块,但根据导入的方式,您仍然可以找到不起作用的情况:

from ast import ClassDef, parse, ImportFrom
import importlib
import inspect

mod = "test"
mod = importlib.import_module(mod)
p = parse(inspect.getsource(mod))
names = []
for node in p.body:
    if isinstance(node, ClassDef):
        names.append(node.name)
    elif isinstance(node, ImportFrom):
        names.extend(imp.name for imp in node.names)

print(names)

输入:

from test2 import Foobar, Barbar, foo  

class Foo(object):
   pass

class Bar(object):
    pass

测试2:

foo = 123

class Foobar(object):
    pass

class Barbar(object):
    pass

输出:

['Foobar', 'Barbar', 'Foo', 'Bar']

【讨论】:

    【解决方案2】:

    我尝试了built-in type function,它对我有用,但可能有一种更 Python 的方式来测试一个类的存在:

    import types
    
    def class_exist(className):
        result = False
        try:
            result = (eval("type("+className+")") == types.ClassType)
        except NameError:
            pass
        return result
    
    # this is a test class, it's only purpose is pure existence:
    class X: 
        pass
    
    print class_exist('X')
    print class_exist('Y')
    

    输出是

    True
    False
    

    当然,这是一个基本的解决方案,只能用于众所周知的输入:eval 函数can 是一个很好的后门开启者。有一个更可靠(但也很紧凑)solution by wenzul

    【讨论】:

    • 谢谢!那行得通。它比遍历模块查找可用类要容易得多。你拯救了我的一天:)
    • 对任何尝试此操作的人只有一条评论:确保您导入了类本身,而不仅仅是包含它的模块。否则,如果您在模块 Y 中定义类 X,检查将失败。我可能应该尝试 class_exist('Y.X'),但这并不重要。如果您执行“从 Y 导入 X”,您的课程将可用并且测试会顺利进行
    • 使用eval() 为任意代码执行敞开大门,为了安全起见,应该避免这种情况。
    • @Alexandr 尝试使用自己的打印功能 injectme()class_exist("injectme()")。当然,攻击者不能轻易创建函数对象。但这应该指出可能会执行无意的代码。
    • @wenzul 是的,我知道这一点,这就是我问的原因。如果我使用这种方法,我会严格避免用户输入为evaluated。我添加了免责声明,其中包含指向您的解决方案的链接。
    【解决方案3】:

    使用eval() 为任意代码执行敞开大门,为了安全起见,应该避免这种情况。

    特别是如果您在这里要求解决此类问题。 那么我们可以假设您对这些风险没有充分了解。

    import sys
    
    def str_to_class(str):
        return reduce(getattr, str.split("."), sys.modules[__name__])
    
    try:
        cls = str_to_class(<json-fragment-here>)
    except AttributeError:
        cls = None
    
    if cls:
        obj = cls(...)
    else:
        # fight against this
    

    这避免了使用eval,并得到了多个 SO 用户的认可。 解决方法类似于Convert string to Python class object?

    【讨论】:

    • 正如我在回答中指出的那样(我现在必须研究这个)。但也许您的解决方案比问题更普遍?您要求的其他信息尚未到达...
    • 因为我的项目中的 str_to_class 方法在一些 utils 文件中定义,所以我对其进行了一些修改,以检查顶层(调用它的位置)上的类是否存在。所以现在它看起来像: def str_to_class(str): frame = inspect.stack()[-1] module = inspect.getmodule(frame[0]) return reduce(getattr, str.split("."), sys. modules[module.__name__]) 还可以检查确切调用它的方法并从该方法中获取模块名称会很棒...但是对于当前任务来说很好...
    猜你喜欢
    • 2021-10-22
    • 2011-06-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-09-08
    • 2015-08-15
    相关资源
    最近更新 更多