python: получить конструктор, чтобы вернуть существующий объект вместо нового - PullRequest
0 голосов
/ 01 сентября 2010

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

class X:
    def __new__(cls, arg):
        i = f(arg)
        if i:
            return X._registry[i]
        else:
            return object.__new__(cls)
    # more stuff here (such as __init_, _registry, etc.)

Конечно, если выполняется первая ветвь, мне не нужно __init__, но она все равно вызывается. Какой хороший способ сказать __init__ ничего не делать?

Возможно, я могу просто добавить какой-нибудь атрибут, чтобы отслеживать, запущен ли __init__, но, возможно, есть лучший способ?

Ответы [ 2 ]

3 голосов
/ 01 ноября 2015

В языках, которые поддерживают частные конструкторы (C #, Dart, Scala и т. Д.), Фабричные методы обеспечивают надежное решение этой проблемы.

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

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

class Unique(type):

    def __call__(cls, *args, **kwargs):
        if args[0] not in cls._cache:
            self = cls.__new__(cls, *args, **kwargs)
            cls.__init__(self, *args, **kwargs)
            cls._cache[args[0]] = self
        return cls._cache[args[0]]

    def __init__(cls, name, bases, attributes):
        super().__init__(name, bases, attributes)
        cls._cache = {}

. Он может использоваться следующим образом:

class Country(metaclass=Unique):
    def __init__(self, name: str, population: float, nationalDish: str):
        self.name = name
        self.population = population
        self.nationalDish = nationalDish

placeA = Country("Netherlands", 16.8e6, "Stamppot")
placeB = Country("Yemen", 24.41e6, "Saltah")
placeC = Country("Netherlands", 11, "Children's tears")

print(placeA is placeB)    # -> False
print(placeA is placeC)    # -> True
print(placeC.nationalDish) # -> Stamppot

В целом, этот подход полезенесли вы хотите создать набор уникальных объектов во время выполнения (возможно, используя данные, в которых записи могут повторяться).

1 голос
/ 01 сентября 2010

Используйте фабрику, то есть

_x_singleton = None

def XFactory():
    global _x_singleton

    if _x_singleton is None:
        _x_singleton = X()
    return _x_singleton

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

class X(object):
    instance = None
    def __init__(self):
        # ...
    @classmethod
    def create(cls):
        if cls.instance is None:
            cls.instance = cls()
        return cls.instance

Вы можете даже подумать__init__ вызвать исключение, если какое-то условие не выполнено (т. Е. Self.instance не равно None)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...