Repository: microsoft/qlib
File: qlib/utils/serial.py (Python)

Code before:
        for k, v in params.items():
            if v is not None:
                attr_name = f"_{k}"
                setattr(self, attr_name, v)

        if recursive:
            for obj in self.__dict__.values():
                # set flag to prevent endless loop
                self.__dict__[self.FLAG_KEY] = True
                if isinstance(obj, Serializable) and self.FLAG_KEY not in obj.__dict__:
                    obj.config(**params, recursive=True)
                del self.__dict__[self.FLAG_KEY]

    def to_pickle(self, path: Union[Path, str], dump_all: bool = None, exclude: list = None):
        """
        Dump self to a pickle file.

        Args:
            path (Union[Path, str]): the path to dump
            dump_all (bool, optional): if need to dump all things. Defaults to None.
            exclude (list, optional): will exclude the attributes in this list when dumping. Defaults to None.
        """
        self.config(dump_all=dump_all, exclude=exclude)
        with Path(path).open("wb") as f:
            self.get_backend().dump(self, f, protocol=C.dump_protocol_version)

    @classmethod
    def load(cls, filepath):
        """
        Load the serializable class from a filepath.

        Args:
            filepath (str): the path of file

        Raises:
            TypeError: the pickled file must be `type(cls)`

        Returns:
            `type(cls)`: the instance of `type(cls)`
        """
        with open(filepath, "rb") as f:
            object = cls.get_backend().load(f)
        if isinstance(object, cls):
            return object
        else:
            raise TypeError(f"The instance of {type(object)} is not a valid `{type(cls)}`!")

    @classmethod
    def get_backend(cls):
        """
        Return the real backend of a Serializable class. The pickle_backend value can be "pickle" or "dill".

Review comment (suggestion):
The backend may not be a pickle.
Maybe you can add  `Serializable.dump_kwargs`  for dumping and encourage users to override it.

Code after:
        for k, v in params.items():
            if v is not None:
                attr_name = f"_{k}"
                setattr(self, attr_name, v)

        if recursive:
            for obj in self.__dict__.values():
                # set flag to prevent endless loop
                self.__dict__[self.FLAG_KEY] = True
                if isinstance(obj, Serializable) and self.FLAG_KEY not in obj.__dict__:
                    obj.config(**params, recursive=True)
                del self.__dict__[self.FLAG_KEY]

    def to_pickle(self, path: Union[Path, str], dump_all: bool = None, exclude: list = None):
        """
        Dump self to a pickle file.

        Args:
            path (Union[Path, str]): the path to dump
            dump_all (bool, optional): if need to dump all things. Defaults to None.
            exclude (list, optional): will exclude the attributes in this list when dumping. Defaults to None.
        """
        self.config(dump_all=dump_all, exclude=exclude)
        with Path(path).open("wb") as f:
            # pickle interface like backend; such as dill
            self.get_backend().dump(self, f, protocol=C.dump_protocol_version)

    @classmethod
    def load(cls, filepath):
        """
        Load the serializable class from a filepath.

        Args:
            filepath (str): the path of file

        Raises:
            TypeError: the pickled file must be `type(cls)`

        Returns:
            `type(cls)`: the instance of `type(cls)`
        """
        with open(filepath, "rb") as f:
            object = cls.get_backend().load(f)
        if isinstance(object, cls):
            return object
        else:
            raise TypeError(f"The instance of {type(object)} is not a valid `{type(cls)}`!")

    @classmethod
    def get_backend(cls):
        """