Метод класса, реализованный в родительском классе, возвращающий имя дочернего класса

Я хотел бы создать метод класса или абстрактный метод в родительском классе. Этот метод должен возвращать имя класса реализации.

Например, используя метод класса:

class TestParentClass():
    somefield = ""
    
    # create a method to get the name of this class.
    @classmethod
    def get_name(cls):
        class_name = cls.__name__
        return class_name


class TestClass(TestParentClass):
    somefield = TestParentClass.get_name()


print(TestClass().somefield) # I want this to print 'TestClass'

Я бы хотел, чтобы оператор печати печатал TestClass, но это будет печататься TestParentClass.

Я также попробовал использовать абстрактный метод, например:

class TestParentClass():
    somefield = ""
    
    # create a method to get the name of this class.
    @abstractmethod
    def get_name():
        class_name = __class__.__name__
        print(class_name)
        return class_name

Однако и это будет напечатано TestParentClass.

Использование конструктора в дочернем классе также не делает ничего другого, т.е.:

class TestClass(TestParentClass):
    def __init__(self) -> None:
        somefield = TestParentClass.get_name() # still sets 'TestParentClass'

Есть ли способ сделать это в Python?

Другими словами, мне бы хотелось чего-то эквивалентного этому решению С#:

class TestParentClass
{
    public string SomeField { get; set; }

    public string GetName()
    {
        return this.GetType().Name;
    }
}

class TestClass : TestParentClass
{
    public TestClass()
    {
        SomeField = this.GetName();
    }
}

void Main()
{
    Console.WriteLine(new TestClass().SomeField); // This prints TestClass.
}

🤔 А знаете ли вы, что...
Синтаксис Python известен своей простотой и читаемостью.


50
3

Ответы:

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

Способ сделать это — использовать метод класса в родительском классе, а также реализовать в родительском классе конструктор, который устанавливает поле. Например:

class TestParentClass():
    @classmethod
    def get_name(cls):
        class_name = cls.__name__
        return class_name
    
    def __init__(self):
        self.somefield = self.get_name()

class TestClass(TestParentClass):
    pass

print(TestClass().somefield) # This prints 'TestClass'

Решено

somefield может быть определен с помощью __init_subclass__ (который является особым методом класса без необходимости украшать его как таковой).

class TestParentClass:
    def __init_subclass__(cls, **kwargs):
        super().__init_subclass__(**kwargs)
        cls.somefield = cls.__name__
   

class TestClass(TestParentClass):
    pass


print(TestClass().somefield)

Почему бы вам не использовать свойство вместо метода?

class TestParentClass:

    @property
    def somefield(self):
        return self.__class__.__name__


class TestClass(TestParentClass):
    pass

Это должно печатать TestClass при вызове print(TestClass().somefield).

Или, если вам нужно на уровне класса:

class classproperty(property): # noqa
    def __get__(self, owner_self, owner_cls): # noqa
        return self.fget(owner_cls) # noqa


class TestParentClass:

    @classproperty
    def somefield(cls):
        class_name = cls.__name__
        return class_name


class TestClass(TestParentClass):
    pass

Это будет работать как для print(TestClass().somefield), так и для print(TestClass.somefield).