如何获得绑定方法的定义类?

时间:2018-12-21 19:27:08

标签: python python-3.x

我被设置为AutoRepr类和decorator类...

class AutoRepr:
    def __repr__(self):
        def _fix(thing):
            if isinstance(thing, str):
                return f'"{thing}"'
            if isinstance(thing, Iterable):
                s = str(thing)
                if len(s) > 30:
                    return type(thing)
                else:
                    return s
            return thing

        props = []
        try:
            for attr in self.__slots__:
                if attr.startswith('_'):
                    continue
                try:
                    attr_val = getattr(self, attr)
                    if attr_val:
                        props.append(f'{attr}={_fix(attr_val)}')
                except AttributeError:
                    pass
        except AttributeError:
            props = [f'{k}={_fix(v)}'
                     for k, v in self.__dict__.items()
                     if not k.startswith('_')]
        return f'{type(self).__name__}({", ".join(props)})'


def auto_repr(override_all=False):
    def decorator(cls):
        repr_defined_in = cls.__repr__.__qualname__.split('.')[0]
        if not override_all and repr_defined_in == cls.__name__:
            # repr overriden in class. Don't mess with it
            return cls
        cls.__repr__ = AutoRepr.__repr__
        return cls

    return decorator

# Example 1
@auto_repr()
class MyClass:
    def __init__(self):
        self.strength = None
        self.weakness = 'cake'

# Example 2
class Another(AutoRepr):
    __slots__ = ('num', 'my_list')
    def __init__(self):
        self.num = 12
        self.my_list = [1, 2, 3]


f = MyClass()
print(f)

b = Another()
print(b)

# MyClass(strength=None, weakness="cake")
# Another(num=12, my_list=[1, 2, 3])

在装饰器中,我需要检查包装的类__repr__是否在该类中被重写或属于父类。如果__repr__已被该类覆盖,那么我不希望auto_repr做任何事情,但是,如果不是,那么显然希望auto-repr做这件事。通过通过__qualname__比较repr方法绑定类的字符串名称,我设法找到了一个解决方案,但是它感觉便宜又笨重。理想情况下,我想正确if cls is repr_defined_in_cls检查身份。

我所见过的所有SO问题仅针对获取类的字符串名称,而不是用于比较的类。有没有更好的方法来获取定义该方法的(原始)类?

2 个答案:

答案 0 :(得分:2)

检查类__dict__的{​​{1}}键:

"__repr__"

答案 1 :(得分:2)

当然,步行mro并确保您击中的第一个__repr__不是来自object的人:

In [18]: class A:
    ...:     pass
    ...:
    ...: class B(A):
    ...:     def __repr__(self): return "a repr"
    ...:
    ...: class C(B):
    ...:     pass
    ...:
    ...:

In [19]: def is_non_default_repr(klass):
    ...:     for superclass in klass.mro():
    ...:         if '__repr__' in vars(superclass):
    ...:             return superclass is not object
    ...:     return False # should we ever get here? maybe raise error?
    ...:
    ...:

In [20]: is_non_default_repr(A)
Out[20]: False

In [21]: is_non_default_repr(B)
Out[21]: True

In [22]: is_non_default_repr(C)
Out[22]: True

我认为,如果一个元类使用__slots__,这将失败,但这将是一种畸变。

编辑:

重新阅读您的要求,您可以执行以下操作:

In [23]: def which_method(klass, methodname):
    ...:     for superclass in klass.mro():
    ...:         if methodname in vars(superclass):
    ...:             return superclass
    ...:     raise ValueError(f"{methodname} not a member")
    ...:
    ...:

In [24]: which_method(C, '__repr__') is not object
Out[24]: True

In [25]: which_method(C, '__repr__')
Out[25]: __main__.B

In [26]: which_method(C, '__repr__') is C
Out[26]: False

In [27]: which_method(B, '__repr__') is B
Out[27]: True

完全取决于您所需的语义。我想你明白了。