用于重构类源的python类的Str方法

时间:2014-06-03 17:51:16

标签: python python-2.7 fixtures quine

如何为类配备一个__str__方法,该方法打印类本身的来源,不包括方法定义

我排除方法定义,因为我的用例是我动态生成灯具(使用fixtures包;这与django灯具不同),我想将这些灯具存储在文件中。对于那些不知道的人,fixtures包使用python类来表示夹具数据。

这似乎甚至可以用于固定装置,因为在我看来它将是一个适度的常用用例。

1 个答案:

答案 0 :(得分:1)

您可以使用元类来完成此操作。这里我省略了private(以下划线开头)和可调用对象(方法,内部类等)。仅包含直接在类上定义的属性(基类已正确列出,因此这不应成为问题)。

class ClassSourceMeta(type):
    def __str__(cls):
         return "class %s(%s):\n\n%s\n" % (cls.__name__, 
                ", ".join(k.__name__ for k in cls.__bases__), 
                "\n".join("    %s = %r" % (k, v) for (k, v) in
                cls.__dict__.items() if not (k.startswith("_") or callable(v))))
    __repr__ = __str__    

class Mine(object):
    __metaclass__ = ClassSourceMeta
    a = 42
    b = 13

print(Mine)

在Python 3中,您需要以另一种方式指定元类:

class Mine(metaclass=ClassSourceMeta):
    a = 42
    b = 13

请注意,属性需要具有合理的repr() s。如果它们是类实例,那么除非您为它们编写好的a = <__main__.Mine object at 0x02AA4870>方法,否则最终可能会出现语法无效的内容,例如__repr__。对于基本的东西,比如数字和字符串,甚至是列表和字母,你都会被覆盖。

另请注意,属性不一定按照定义的顺序排列;属性存储在dict中,不保留插入顺序。不过,Python并不在乎。

如果fixtures模块已经使用了元类,那么您可能需要继承它而不是type,或者修补它们的元类。在Python 2.x上,这将需要为__str__类创建一个方法包装器:

from types import MethodType

def __str__(cls):
     return "class %s(%s):\n\n%s\n" % (cls.__name__, 
            ", ".join(k.__name__ for k in cls.__bases__), 
            "\n".join("    %s = %r" % (k, v) for (k, v) in
            cls.__dict__.items() if not (k.startswith("_") or callable(v))))

 MetaclassToPatch.__str__ = MethodType(__str__, None, MetaclassToPatch)
 MetaclassToPatch.__repr__ = MetaclassToPatch.__str__