У меня есть класс с именем A:

>>> class A:
    def __init__(self):
        self.register = {}


>>> 

Класс A будет подразделен на класс B. Однако класс B содержит методы, которые необходимо зарегистрировать как пару name: function в экземплярах словаря класса A. Это так, класс A может выполнять работу с использованием методов.

Вот пример того, что я имею в виду:

>>> class B(A):
    def foo(self):
        pass
    def bar(self):
        pass


>>> b = B()
>>> b.register # foo and bar were registered
{'key': <foo function>, 'key2': <bar function>}
>>> 

Есть ли идиоматический способ решить эту проблему? Или что-то подобное невозможно, и было бы лучше изменить структуру моего кода.


Обратите внимание, что это не дубликат Методы автоматической регистрации класса с использованием decorator, поскольку мой регистр не является глобальным, это переменная экземпляра класса. Это означает, что использование метакласса, подобного показанному в выбранном ответе, не будет работать.

0
Christian Dean 27 Май 2017 в 21:39

2 ответа

Лучший ответ

Если вы объявите все нужные вам классы, и после этого беспокойства о том, что регистры instance имеют ссылки на все объявленные методы во всех подклассах, вам просто нужно выполнить информацию «регистра» при объявлении самих подклассов. Это легко сделать в Python 3.6 (но не в 3.5) с новым __init_subclass__ механизм. В Python 3.5 и до этого легче выполнять метакласс.

class FallbackDict(dict):
    def __init__(self, fallback):
         self.fallback = fallback
    def __missing__(self, key):
          value = self.fallback[key]
          self[key] = value
          return value

class A:
    register = {}
    def __init__(self):
          # instance register shadows global register
          # for access via "self."
          self.register = FallbackDict(__class__.register)

    def __init_subclass__(cls):
          for attrname, value in cls.__dict__.items():
               if callable(value):
                    __class__.register[attrname] = value

Код здесь должен быть простым - пользовательский класс dict будет «копировать при чтении» значения словаря A.regiser в словарь экземпляров. Если вам нужен более согласованный словарь, включающий это поведение (например, такой, который будет корректно повторять ключи, значения и элементы как самого себя, так и запасного словаря), вам лучше реализовать класс «FallbackDict» как экземпляр коллекций. Вместо этого abc.MutableMapping (и просто использовать сводный словарь для фактического хранения данных)

Вам вообще не нужен пользовательский dict, если вы планируете создать все ваши классы, которые регистрируют новые методы, перед созданием любого экземпляра «A» - или если вновь созданные классы не должны обновлять существующие экземпляры - но в этом случае Вы должны скопировать A.register в «self.register» экземпляра внутри __init__.

Если вы не можете перейти на Python 3.6, вам также понадобится собственный метакласс, чтобы вызвать метод __init_subclass__, описанный выше. Просто оставьте все как есть, чтобы ваш код был готов перейти на Python 3.6, исключить метакласс и добавить метакласс примерно так:

class MetaA(type):
    def __init__(cls, name, bases, namespace):
         super().__init__(name, bases, namespace)
         cls.__init_subclass__()

class A:
    ...
    @clasmethod
    def __init_subclass__(cls):
        # as above
        ...   
1
jsbueno 27 Май 2017 в 21:56

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

Информация, которую вы пытаетесь зарегистрировать , является глобальной информацией. Хотя вы хотите, чтобы у каждого экземпляра был регистр, содержащий эту глобальную информацию, все, что вам действительно нужно сделать, - это __init__ скопировать глобальный регистр в регистр экземпляра.

2
user1084944user1084944 27 Май 2017 в 19:02