简体   繁体   中英

Auto __repr__ method

我想要任何类的简单表示,比如{ property = value } ,是否有 auto __repr__

Simplest way:

def __repr__(self):
    return str(self.__dict__)

Yes, you can make a class "AutoRepr" and let all other classes extend it:

>>> class AutoRepr(object):
...     def __repr__(self):
...         items = ("%s = %r" % (k, v) for k, v in self.__dict__.items())
...         return "<%s: {%s}>" % (self.__class__.__name__, ', '.join(items))
... 
>>> class AnyOtherClass(AutoRepr):
...     def __init__(self):
...         self.foo = 'foo'
...         self.bar = 'bar'
...
>>> repr(AnyOtherClass())
"<AnyOtherClass: {foo = 'foo', bar = 'bar'}>"

Note that the above code will not act nicely on data structures that (either directly or indirectly) reference themselves. As an alternative, you can define a function that works on any type:

>>> def autoRepr(obj):
...     try:
...         items = ("%s = %r" % (k, v) for k, v in obj.__dict__.items())
...         return "<%s: {%s}." % (obj.__class__.__name__, ', '.join(items))
...     except AttributeError:
...         return repr(obj)
... 
>>> class AnyOtherClass(object):
...     def __init__(self):
...         self.foo = 'foo'
...         self.bar = 'bar'
...
>>> autoRepr(AnyOtherClass())
"<AnyOtherClass: {foo = 'foo', bar = 'bar'}>"
>>> autoRepr(7)
'7'
>>> autoRepr(None)
'None'

Note that the above function is not defined recursively, on purpose, for the reason mentioned earlier.

Well, I played a little bit with other answers and got a very pretty solution:

class data:
    @staticmethod
    def repr(obj):
        items = []
        for prop, value in obj.__dict__.items():
            try:
                item = "%s = %r" % (prop, value)
                assert len(item) < 20
            except:
                item = "%s: <%s>" % (prop, value.__class__.__name__)
            items.append(item)

        return "%s(%s)" % (obj.__class__.__name__, ', '.join(items))

    def __init__(self, cls):
        cls.__repr__ = data.repr
        self.cls = cls

    def __call__(self, *args, **kwargs):
        return self.cls(*args, **kwargs)

You use it as a decorator:

@data
class PythonBean:
    def __init__(self):
        self.int = 1
        self.list = [5, 6, 7]
        self.str = "hello"
        self.obj = SomeOtherClass()

and get a smart __repr__ out of the box:

PythonBean(int = 1, obj: <SomeOtherClass>, list = [5, 6, 7], str = 'hello')

This works with any recursive classes, including tree structures. If you try to put a self-reference in the class self.ref = self , the function will try (successfully) to work it out for about a second.

Of course, always mind your boss - mine would not like such a syntax sugar ))

Do you mean

__dict__

?

class MyClass:
    def __init__(self, foo: str, bar: int):
        self.foo = foo
        self.bar = bar
        self._baz: bool = True

    def __repr__(self):
        f"{self.__class__.__name__}({', '.join([f'{k}={v}' for k, v in self.__dict__.items() if not k.startswith('_')])})"

mc = MyClass('a', 99)

print(mc)
# MyClass(foo=a, bar=99)
# ^^^ Note that _baz=True was hidden here

I use this helper function to generate repr s for my classes. It is easy to run in a unittest function, ie.

def test_makeRepr(self):
    makeRepr(Foo, Foo(), "anOptional space delimitedString ToProvideCustom Fields")

this should output a number of potential repr to the console, that you can then copy/paste into your class.

def makeRepr(classObj, instance = None, customFields = None):
    """Code writing helper function that will generate a __repr__ function that can be copy/pasted into a class definition.

    Args:
        classObj (class):
        instance (class):
        customFields (string):

    Returns:
        None:

    Always call the __repr__ function afterwards to ensure expected output.
    ie. print(foo)

    def __repr__(self):
        msg = "<Foo(var1 = {}, var2 = {})>"
        attributes = [self.var1, self.var2]
        return msg.format(*attributes)
    """ 
    if isinstance(instance, classObj):
        className = instance.__class__.__name__
    else:
        className=classObj.__name__

    print('Generating a __repr__ function for: ', className,"\n")
    print("\tClass Type: "+classObj.__name__, "has the following fields:")
    print("\t"+" ".join(classObj.__dict__.keys()),"\n")
    if instance:
        print("\tInstance of: "+instance.__class__.__name__, "has the following fields:")
        print("\t"+" ".join(instance.__dict__.keys()),"\n")
    else:
        print('\tInstance of: Instance not provided.\n')

    if customFields:
        print("\t"+"These fields were provided to makeRepr:")
        print("\t"+customFields,"\n")
    else:
        print("\t"+"These fields were provided to makeRepr: None\n")
    print("Edit the list of fields, and rerun makeRepr with the new list if necessary.\n\n")

    print("repr with class type:\n")
    classResult = buildRepr( classObj.__name__, " ".join(classObj.__dict__.keys()))
    print(classResult,"\n\n")

    if isinstance(instance, classObj):
        instanceResult = buildRepr( instance.__class__.__name__, " ".join(instance.__dict__.keys()))
    else:
        instanceResult = "\t-----Instance not provided."
    print("repr with instance of class:\n")
    print(instanceResult,"\n\n") 

    if customFields:
        customResult = buildRepr( classObj.__name__, customFields)
    else:
        customResult = '\t-----Custom fields not provided'
    print("repr with custom fields and class name:\n")
    print(customResult,"\n\n")    

    print('Current __repr__')
    print("Class Object: ",classObj)
    if instance:
        print("Instance: ",instance.__repr__())
    else:
        print("Instance: ", "None")


def buildRepr(typeName,fields):
    funcDefLine = "def __repr__(self):"
    msgLineBase  = '    msg = "<{typename}({attribute})>"'
    attributeListLineBase = '    attributes = [{attributeList}]'
    returnLine = '    return msg.format(*attributes)'
    x = ['self.' + x for x in fields.split()]
    xResult = ", ".join(x)
    y = [x + ' = {}' for x in fields.split()]
    yResult = ', '.join(y)
    msgLine = msgLineBase.format(typename = typeName, attribute = yResult)
    attributeListLine = attributeListLineBase.format(attributeList = xResult) 
    result = "{declaration}\n{message}\n{attributes}\n{returnLine}".format(declaration = funcDefLine,
                                                                       message = msgLine,
                                                                       attributes = attributeListLine,
                                                                       returnLine =returnLine )
    return result

You can compress voddan's answer to :

class data:
    def __init__(self, cls):
        cls.__repr__ = lambda obj : f"{obj.__class__.__name__}({', '.join([f'{prop}={value}' for prop, value in obj.__dict__.items()])})"
        self.cls = cls

    def __call__(self, *args, **kwargs):
        return self.cls(*args, **kwargs)

Example of use:

@data
class X:
    def __init__(self, x, y):
        self.x = x
        self.y = y
        self.z = x ^ y

print(X(3, 2))
# X(x=2, y=3, z=1)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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