我有一些类看起来像这样:
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子类,因为它们通过元类有效地自动"注册" - 但只会在列表中留下任何其他内容.这可以被视为错误或功能.;¬)
| 归档时间: |
|
| 查看次数: |
25033 次 |
| 最近记录: |