backtrader代码第二步 —— MetaParams类解读

源代码阅读第二步:

class Cerebro(with_metaclass(MetaParams, object)):
    pass 

backtrader最重要的一个类是Cerebro(译为大脑)。

在第一步中我们认识了with_metaclass, Cerebro的类型为 MetaParams

cerebro = MetaParams.__new__(name, bases, d)

MetaParams源代码

class MetaParams(MetaBase):
    def __new__(meta, name, bases, dct):
        # Remove params from class definition to avod inheritance
        # (and hence "repetition")
        newparams = dct.pop('params', ())
        packs = 'packages'
        newpackages = tuple(dct.pop(packs, ()))  # remove before creation
        fpacks = 'frompackages'
        fnewpackages = tuple(dct.pop(fpacks, ()))  # remove before creation
        # Create the new class - this pulls predefined "params"
        cls = super(MetaParams, meta).__new__(meta, name, bases, dct)
        # Pulls the param class out of it - default is the empty class
        params = getattr(cls, 'params', AutoInfoClass)
        # Pulls the packages class out of it - default is the empty class
        packages = tuple(getattr(cls, packs, ()))
        fpackages = tuple(getattr(cls, fpacks, ()))
        # get extra (to the right) base classes which have a param attribute
        morebasesparams = [x.params for x in bases[1:] if hasattr(x, 'params')]
        # Get extra packages, add them to the packages and put all in the class
        for y in [x.packages for x in bases[1:] if hasattr(x, packs)]:
            packages += tuple(y)
        for y in [x.frompackages for x in bases[1:] if hasattr(x, fpacks)]:
            fpackages += tuple(y)
        cls.packages = packages + newpackages
        cls.frompackages = fpackages + fnewpackages
        # Subclass and store the newly derived params class
        cls.params = params._derive(name, newparams, morebasesparams)
        return cls
    def donew(cls, *args, **kwargs):
        clsmod = sys.modules[cls.__module__]
        # import specified packages
        for p in cls.packages:
            if isinstance(p, (tuple, list)):
                p, palias = p
            else:
                palias = p
            pmod = __import__(p)
            plevels = p.split('.')
            if p == palias and len(plevels) > 1:  # 'os.path' not aliased
                setattr(clsmod, pmod.__name__, pmod)  # set 'os' in module
            else:  # aliased and/or dots
                for plevel in plevels[1:]:  # recurse down the mod
                    pmod = getattr(pmod, plevel)
                setattr(clsmod, palias, pmod)
        # import from specified packages - the 2nd part is a string or iterable
        for p, frompackage in cls.frompackages:
            if isinstance(frompackage, string_types):
                frompackage = (frompackage,)  # make it a tuple
            for fp in frompackage:
                if isinstance(fp, (tuple, list)):
                    fp, falias = fp
                else:
                    fp, falias = fp, fp  # assumed is string
                # complain "not string" without fp (unicode vs bytes)
                pmod = __import__(p, fromlist=[str(fp)])
                pattr = getattr(pmod, fp)
                setattr(clsmod, falias, pattr)
        # Create params and set the values from the kwargs
        params = cls.params()
        for pname, pdef in cls.params._getitems():
            setattr(params, pname, kwargs.pop(pname, pdef))
        # Create the object and set the params in place
        _obj, args, kwargs = super(MetaParams, cls).donew(*args, **kwargs)
        _obj.params = params
        _obj.p = params  # shorter alias
        # Parameter values have now been set before __init__
        return _obj, args, kwargs

父类MetaBase源代码

class MetaBase(type):
    def doprenew(cls, *args, **kwargs):
        return cls, args, kwargs
    def donew(cls, *args, **kwargs):
        _obj = cls.__new__(cls, *args, **kwargs)
        return _obj, args, kwargs
    def dopreinit(cls, _obj, *args, **kwargs):
        return _obj, args, kwargs
    def doinit(cls, _obj, *args, **kwargs):
        _obj.__init__(*args, **kwargs)
        return _obj, args, kwargs
    def dopostinit(cls, _obj, *args, **kwargs):
        return _obj, args, kwargs
    def __call__(cls, *args, **kwargs):
        cls, args, kwargs = cls.doprenew(*args, **kwargs)