Maybe you could add some additional methods for the optimizer instance:
# -*- coding: utf-8 -*-
# @Time    : 2020/12/19
# @Author  : Lart Pang
# @GitHub  : https://github.com/lartpang
import types

from torch import nn
from torch.optim import SGD, Adam, AdamW


class OptimizerConstructor:
    def __init__(self, model, initial_lr, mode, group_mode, cfg):
        """
        A wrapper of the optimizer.

        :param model: nn.Module
        :param initial_lr: int
        :param mode: str
        :param group_mode: str
        :param cfg: A dict corresponding to your ``mode`` except the ``params`` and ``lr``.
        """

        self.mode = mode
        self.initial_lr = initial_lr
        self.group_mode = group_mode
        self.cfg = cfg
        self.params = self.group_params(model)

    def construct_optimizer(self):
        if self.mode == "sgd":
            optimizer = SGD(params=self.params, lr=self.initial_lr, **self.cfg)
        elif self.mode == "adamw":
            optimizer = AdamW(params=self.params, lr=self.initial_lr, **self.cfg)
        elif self.mode == "adam":
            optimizer = Adam(params=self.params, lr=self.initial_lr, **self.cfg)
        else:
            raise NotImplementedError
        return optimizer

    def group_params(self, model):
        ....

    def __call__(self):
        optimizer = self.construct_optimizer()
        optimizer.lr_groups = types.MethodType(get_lr_groups, optimizer)
        optimizer.lr_string = types.MethodType(get_lr_strings, optimizer)
        return optimizer

def get_lr_groups(self):
    return [group["lr"] for group in self.param_groups]

def get_lr_strings(self):
    return ",".join([f"{group['lr']:10.3e}" for group in self.param_groups])