前进声明课程?

paf*_*fcu 37 python

我有一些类看起来像这样:

class Base:
  subs = [Sub3,Sub1]
  # Note that this is NOT a list of all subclasses!
  # Order is also important

class Sub1(Base): pass
class Sub2(Base): pass
class Sub3(Base): pass
...
Run Code Online (Sandbox Code Playgroud)

现在,这失败了,因为Base.subs时没有定义Sub1和Sub3.但显然我不能把子类放在Base之前.有没有办法在Python中转发声明类?我想使用,isinstance因此subs中的类型实际上必须与后面声明的子类相同,它们具有相同的名称和其他属性是不够的.

一个解决方法是:Base.subs = [Sub3,Sub1] 定义了子类之后,但我不喜欢以这种方式拆分我的类.

编辑:添加了有关订单的信息

Ign*_*ams 12

编写一个装饰器,将其添加到注册表中Base.

class Base(object):
  subs = []

  @classmethod
  def addsub(cls, scls):
    cls.subs.append(scls)

 ...

@Base.addsub
class Sub1(Base):
  pass

class Sub2(Base):
  pass

@Base.addsub
class Sub3(Base):
  pass
Run Code Online (Sandbox Code Playgroud)


mar*_*eau 12

这里基本上是@Ignacio Vazquez-Abrams的混合版本和@ aaronasterling的答案,它们保留了列表中子类的顺序.最初,所需的子类名称(即字符串)subs按所需顺序手动放置在列表中,然后在定义每个子类时,类装饰器使相应的字符串替换为实际的子类:

class Base(object):  # New-style class (i.e. explicitly derived from object).

    @classmethod
    def register_subclass(cls, subclass):
        """ Class decorator for registering subclasses. """

        # Replace any occurrences of the class name in the class' subs list.
        # with the class itself.
        # Assumes the classes in the list are all subclasses of this one.
        # Works because class decorators are called *after* the decorated class'
        # initial creation.
        while subclass.__name__ in cls.subs:
            cls.subs[cls.subs.index(subclass.__name__)] = subclass

        return cls  # Return modified class.

    subs = ['Sub3', 'Sub1']  # Ordered list of subclass names.


@Base.register_subclass
class Sub1(Base): pass

@Base.register_subclass
class Sub2(Base): pass

@Base.register_subclass
class Sub3(Base): pass

print('Base.subs: {}'.format(Base.subs))
# Base.subs: [<class '__main__.Sub3'>, <class '__main__.Sub1'>]
Run Code Online (Sandbox Code Playgroud)

更新

完全相同的事情也可以使用元类来完成 - 它的优点是它不需要显式地装饰每个子类,如上面显示的(你接受的)我的原始答案中所示,但它使所有这些都自动发生.请注意,即使__init__()调用元类' 来创建每个子类,它只会subs在子类'name出现在其中时更新列表 - 所以subs列表内容的初始Base类'定义仍然控制在其中替换的内容(维持秩序).

class BaseMeta(type):

    def __init__(cls, name, bases, classdict):
        if classdict.get('__metaclass__') is not BaseMeta:  # Metaclass instance?
            # Replace any occurrences of a subclass' name in the class being
            # created the class' sub list with the subclass itself.
            # Names of classes which aren't direct subclasses will be ignored.
            while name in cls.subs:
                cls.subs[cls.subs.index(name)] = cls

        # Chain to __init__() of the class instance being created after changes.
        # Note class instance being defined must be new-style class.
        super(BaseMeta, cls).__init__(name, bases, classdict)


# Python 2 metaclass syntax.
class Base(object):  # New-style class (derived from built-in object class).
    __metaclass__ = BaseMeta
    subs = ['Sub3', 'Sub1']  # Ordered list of subclass names.

# Python 3 metaclass syntax.
#class Base(metaclass=BaseMeta):
#    subs = ['Sub3', 'Sub1']  # Ordered list of subclass names.


# Note: No need to manually register the (direct) subclasses.
class Sub1(Base): pass
class Sub2(Base): pass
class Sub3(Base): pass

print('Base.subs: {}'.format(Base.subs))
Run Code Online (Sandbox Code Playgroud)

重要的是要注意这两个答案之间至少有一个微妙的区别 - 即第一个将与通过注册的任何类名一起使用@Base.register_subclass(),无论它实际上是否是其子类Base(尽管可能有可能更改/修复) .)

我指出这一点有几个原因:第一,因为在你的评论中你说这subs是"列表中的一堆类,其中一些可能是它的子类",更重要的是,因为事实并非如此.我的更新中的代码,仅适用于Base子类,因为它们通过元类有效地自动"注册" - 但只会在列表中留下任何其他内容.这可以被视为错误或功能.;¬)