Экземпляр класса как член класса того же класса - PullRequest
0 голосов
/ 04 января 2019

Какой самый «питонный» способ создать члена класса, который является экземпляром этого класса? То есть как то так:

class MyClass:

    # error! (and this is to be expected as MyClass is not yet fully defined)
    instance_as_member = MyClass()

    def __init__(self):
        print("MyClass instance created!")

Это можно решить, добавив член после определения класса:

class MyClass:
    ...
MyClass.instance_as_member = MyClass()

но это кажется немного неправильным; естественно, члены класса должны быть определены в этого класса.

Есть ли лучший способ сделать это?

Ответы [ 3 ]

0 голосов
/ 04 января 2019

Похоже, вам нужен тип setUp функциональности. Вот «немного грязный» способ сделать это из класса: установить флаг переменной класса, чтобы обозначить первое использование класса. Проверьте флаг при создании экземпляра; если это первый доступ, снимите флажок и запустите метод настройки.

class MyClass:

    first_touch = True

    def __init__(self, id=0):

        if MyClass.first_touch:
            MyClass.setUp(id)
        print("MyClass instance created!", id)

    @classmethod
    def setUp(self, id):
        MyClass.first_touch = False
        MyClass.instance_as_member = MyClass(-999)


print("Start")
local_obj = MyClass(1)
local_obj = MyClass(2)
local_obj = MyClass(3)
0 голосов
/ 04 января 2019

Хотя класс не определен в теле, он равен , определен внутри __init__

Однако существует проблема, заключающаяся в том, что создание экземпляра класса внутри __init__ является рекурсивным, поэтому вам необходимо отследить, был ли instance_as_member выполнен.

Что-то вроде следующих работ:

class Foo:
    member_added = False
    instance_as_member = None

    def __init__(self):
        if not Foo.member_added:
            Foo.member_added = True
            Foo.instance_as_member = Foo()

print(Foo.instance_as_member)
Foo()
print(Foo.instance_as_member)

В результате:

нет
<__ main__.Foo объект в 0x7fcd9f68f3c8>

( Вопрос о том, является ли это хорошей идеей, является совершенно другим вопросом! )

0 голосов
/ 04 января 2019

Это не «немного неправильно» - это простой и очевидный способ сделать это. Одна строка кода, понятная каждому и очевидная "что это такое".

Тем не менее, это не "чувствовать" элегантно. Итак, если вы не хотите делать это только для внешнего вида, и, возможно, если есть много таких классов, не повторяющих код, это можно сделать с помощью декоратора классов:

def instance_as_member(attr_name='instance_as_member', *init_args, **init_kwargs):
    def decorator(cls):
        instance = cls(*init_args, **init_kwargs)
        setattr(cls, attr_name, instance)
        return cls
    return decorator

@instance_as_member()
class MyClass:
    ...

Декораторы в порядке, так как они получают cls для модификации как parmater после того, как он полностью создан. С метаклассами это было бы сложно, потому что некоторые шаги создания экземпляров класса, такие как вызов __init_subclass__, выполняются после любых явных методов метакласса, которые могут быть переопределены, поэтому могут возникнуть проблемы при попытке создать экземпляр внутри метод инициализации метакласса (__init__, __new__ или метамета-класс '__call__)

...