эквивалент python частичной части functools для класса/конструктора

Я хочу создать класс, который ведет себя как collection.defaultdict, без указания кода использования. EG: вместо

class Config(collections.defaultdict):
    pass

это:

Config = functools.partial(collections.defaultdict, list)

Это почти работает, но

isinstance(Config(), Config)

выходит из строя. Я уверен, что эта подсказка означает, что в ней есть более острые проблемы. Так есть ли способ добиться этого?

Я также пробовал:

class Config(Object):
    __init__ = functools.partial(collections.defaultdict, list)

Ответ 1

Я не думаю, что существует стандартный метод для этого, но если вам это нужно часто, вы можете просто создать свою небольшую функцию:

import functools
import collections


def partialclass(cls, *args, **kwds):

    class NewCls(cls):
        __init__ = functools.partialmethod(cls.__init__, *args, **kwds)

    return NewCls


if __name__ == '__main__':
    Config = partialclass(collections.defaultdict, list)
    assert isinstance(Config(), Config)

Ответ 2

Если вам действительно нужно работать с явным проверкой типов через isinstance, вы можете просто создать не слишком тривиальный подкласс:

class Config(collections.defaultdict):

    def __init__(self): # no arguments here
        # call the defaultdict init with the list factory
        super(Config, self).__init__(list)

У вас будет конструкция без аргументов со списком фабрики и

isinstance(Config(), Config)

также будет работать.

Ответ 3

У меня была похожая проблема, но я также требовал, чтобы экземпляры моего частично примененного класса были засолены. Я думал, что поделюсь тем, чем я закончил.

Я адаптировал ответ fjarri, посмотрев на собственный Python collections.namedtuple. Приведенная ниже функция создает именованный подкласс, который можно выбрать.

from functools import partialmethod
import sys

def partialclass(name, cls, *args, **kwds):
    new_cls = type(name, (cls,), {
        '__init__': partialmethod(cls.__init__, *args, **kwds)
    })

    # The following is copied nearly ad verbatim from 'namedtuple's' source.
    """
    # For pickling to work, the __module__ variable needs to be set to the frame
    # where the named tuple is created.  Bypass this step in enviroments where
    # sys._getframe is not defined (Jython for example) or sys._getframe is not
    # defined for arguments greater than 0 (IronPython).
    """
    try:
        new_cls.__module__ = sys._getframe(1).f_globals.get('__name__', '__main__')
    except (AttributeError, ValueError):
        pass

    return new_cls