检测类是否定义为声明性或功能性-可能吗?

威姆:

这是一个声明式创建的简单类:

class Person:
    def say_hello(self):
        print("hello")

这是一个类似的类,但是它是通过手动调用元类来定义的:

def say_hello(self):
    print("sayolala")

say_hello.__qualname__ = 'Person.say_hello'

TalentedPerson = type('Person', (), {'say_hello': say_hello})

我很想知道它们是否难以区分。是否可以检测到与类对象本身的差异?

>>> def was_defined_declaratively(cls):
...     # dragons
...
>>> was_defined_declaratively(Person)
True
>>> was_defined_declaratively(TalentedPerson)
False
jsbueno:

完全没有关系。即使我们挖掘更多不同的属性,也应该有可能将这些属性注入动态创建的类中。

现在,即使没有源文件(类似的东西inspect.getsource也可以从中找到内容,但是请参阅下文),类主体语句也应该具有在某个时刻运行的相应“代码”对象。动态创建的类将没有代码体(但是,如果不调用type(...)它,types.new_class也可以为动态类创建一个自定义代码对象-因此,就我的第一句话而言:应该可以使两个类都无法区分。

作为用于定位所述代码对象,而无需依赖于源文件(除了通过,其他inspect.getsource可以通过一个方法的达到.__code__其anotates attibute co_filenameco_fistlineno(I假设一个将必须分析该文件,并找到class在上面的语句co_firstlineno再)

是的,它是这样的:给定一个模块,您可以使用module.__loader__.get_code('full.path.tomodule')-这将返回一个code_object。该对象具有一个co_consts属性,属性是一个序列,其中包含在该模块中编译的所有常量-其中包括类主体本身的代码对象。这些,还有行号和嵌套声明方法的代码对象。

因此,一个简单的实现可能是:

import sys, types

def was_defined_declarative(cls):
    module_name = cls.__module__
    module = sys.modules[module_name]
    module_code = module.__loader__.get_code(module_name)
    return any(
        code_obj.co_name == cls.__name__ 
        for code_obj in module_code.co_consts 
        if isinstance(code_obj, types.CodeType)
    )

对于简单的情况。如果您必须检查类主体是否在另一个函数中,或者嵌套在另一个类主体中,则必须对.co_consts文件中的所有代码对象属性进行递归搜索。如果发现更安全的方法来检查除cls.__name__断言您的课程正确。

再说一次,尽管这对于“表现良好”的类来说是可行的,但有可能在需要时动态创建所有这些属性,但这最终将需要一个属性来替换其中的模块的代码对象,sys.__modules__这会变得有些麻烦而不是简单地提供__qualname__方法。

update此版本比较候选类的所有方法中定义的所有字符串。这将与给定的示例类一起工作-通过比较其他类成员(例如类属性)和其他方法属性(例如变量名,甚至可能是字节码)可以实现更高的准确性。(由于某种原因,模块代码对象和类主体中方法的代码对象是不同的实例,尽管code_objects应该是可插入的)。

我将保留上面的实现,该实现仅比较类名,因为它应该更好地理解正在发生的事情。

def was_defined_declarative(cls):
    module_name = cls.__module__
    module = sys.modules[module_name]
    module_code = module.__loader__.get_code(module_name)
    cls_methods = set(obj for obj in cls.__dict__.values() if isinstance(obj, types.FunctionType))
    cls_meth_strings = [string for method in cls_methods for string in method.__code__.co_consts  if isinstance(string, str)] 

    for candidate_code_obj in module_code.co_consts:
        if not isinstance(candidate_code_obj, types.CodeType):
            continue
        if candidate_code_obj.co_name != cls.__name__:
            continue
        candidate_meth_strings = [string  for method_code in candidate_code_obj.co_consts if isinstance(method_code, types.CodeType) for string in method_code.co_consts if isinstance(string, str)]
        if candidate_meth_strings == cls_meth_strings:
            return True
    return False

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章