【问题标题】:How to build a list of tags from classes in a hierarchy如何从层次结构中的类构建标签列表
【发布时间】:2016-06-13 16:37:41
【问题描述】:

有一个记录器使用“标签”和其他一些带点命名空间的东西来划分事件。我的程序实现了跨各种类的逻辑,其中大部分是继承的,但也有一些是 mixins。

我怎样才能干净地让不同级别的继承建立最终对象可用的项目列表,包括多重继承/混合?重载所有东西并调用子类的东西有点工作(除了mixins),但是有点冗长(如果我做了一些疯狂的MRO事情会更冗长)

class Base(object):
    def tags(self):
        return ['project-base']

class DoThinger(Base):
    def tags(self):
        return super(DoThinger, self).tags() + ['thinger']

class SpecialThinger(DoThinger):
    def tags(self):
        # OK, if verbose
        return super(SpecialThinger, self).tags() + ['special-1']

class SprinklesMixin(object):
    def tags(self):
        return ['sprinkles']

class SprinkleThinger(DoThinger, SprinklesMixin):
    def tags(self):
        # would need to do some magic MRO stuff
        return super(SprinkleThinger, self).tags() + ['specialsprinkles']

print(SpecialThinger().tags())  # -> ['project-base', 'thinger', 'special-1']
print(SprinkleThinger().tags()) # -> ['project-base', 'thinger', 'specialsprinkles']
#                                          (missing 'sprinkles' ^)

理想情况下,我想做类似下面的事情,所以我不会不断重复逻辑,但我不确定... 中的内容。

class Base(object):
    _tags = ['project-base']

    def tags(self):
        ... # something...
        return []

class DoThinger(Base):
    _tags = ['thinger']

class SpecialThinger(DoThinger):
    _tags = ['special-1']

class SprinklesMixin(object):
    _tags = ['sprinkles']

class SprinkleThinger(DoThinger, SprinklesMixin):
    _tags = ['specialsprinkles']

assert set(SpecialThinger().tags()) == {'project-base', 'thinger', 'special-1'}
assert set(SprinkleThinger().tags()) == {'project-base', 'thinger', 'sprinkles', 'specialsprinkles'}

【问题讨论】:

    标签: python multiple-inheritance


    【解决方案1】:

    这是元类的“典型”用例。 Python 中的正常类创建过程使用“类型”元类:它负责创建正确的__mro__ 属性,从而启用多重继承。通过扩展元类行为,我们可以在类创建时扩展 _tags 属性,使用 __mro__

    class MetaBase(type):
        def __init__(cls, name, bases, dct):
            new_tags = set()
            for ancestor in cls.__mro__:
                new_tags.update(getattr(ancestor, "_tags", set()))
            cls.tags = new_tags
    
    class Base(metaclasMetaBase):
        _tags = ['project-base']
    
    
    # In Python 2.x, the class declaration is like:
    class Base(object):
        __metaclass__ = MetaBase
    

    另一种选择是让元类在类中保存_tags 的描述符,在请求时计算每个类的tags_。它应该可以,但可能会产生严重的副作用。就像,这些类的实例可能不会“看到”属性 - 只是当它通过类本身请求时:

    class MetaBase(type):
        @property
        def tags(cls):
            a = set()
            for supercls in cls.__mro__:
                a.update(getattr(supercls, "_tags", set()))
            return a
    

    【讨论】:

    • 我可以将tags 属性/方法移动到基类并执行for ancestor in self.__class__.__mro__: ... 吗?与使用元类相比,这有什么副作用吗?
    • 将它用作类的属性的问题在于,它只有在从每个类的实例中检索时才能正常工作。如果你尝试Base.tags,你会得到“属性对象”而不是你更新的标签集。
    • 可以使用上述元类中的属性并将其与基类上的__getattr__ 方法结合使用,这将允许tags 在类及其实例上工作。
    猜你喜欢
    • 2018-11-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-11-16
    • 2019-01-11
    相关资源
    最近更新 更多