Идиоматический Python для создания нового объекта изнутри класса

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

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

class Foo(object):
    def get_new(self):
        data = # Do interesting things
        return Foo(data)

Однако, если я подкласс Foo и не переопределяю get_new, вызов get_new на SubFoo вернет Foo! Итак, я мог бы написать classmethod:

class Foo(object):

    @classmethod
    def get_new(cls, obj):
        data = # Munge about in objects internals
        return cls(data)

Тем не менее, данные, к которым я обращаюсь, специфичны для объекта, поэтому он, кажется, нарушает инкапсуляцию, чтобы это не было "нормальным" (неразделенным) методом. Кроме того, вы должны называть его как SubFoo.get_new(sub_foo_inst), что кажется излишним. Я хотел бы, чтобы объект просто "знал", какой тип возвращает - тот же тип, что и сам!

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

Итак, мой вопрос: какой лучший способ написать метод, который дает гибкость в типе класса без необходимости комментировать тип повсюду?

Ответ 1

Если вы хотите сделать его более гибким для подкласса, вы можете просто использовать специальный атрибут self.__class__:

class Foo(object):
    def __init__(self, data):
        self.data = data

    def get_new(self):
        data = # Do interesting things
        return self.__class__(data)

Обратите внимание, что использование подхода @classmethod не позволит вам получить доступ к данным в одном экземпляре, удалив его как жизнеспособное решение в случаях, когда #Do interesting things полагается на данные, хранящиеся в экземпляре.

Для Python 2 я не рекомендую использовать type(self), так как это вернет недопустимое значение для классических классов (т.е. те, которые не подклассифицированы из базы object):

>>> class Foo:
...     pass
... 
>>> f = Foo()
>>> type(f)
<type 'instance'>
>>> f.__class__    # Note that the __class__ attribute still works
<class '__main__.Foo'>

Для Python 3 это не такая уж большая проблема, так как все классы получены из object, однако, я считаю, что self.__class__ считается более питонической идиомой.

Ответ 2

Вы можете использовать встроенный тип '.

type(instance)

- это класс экземпляра.