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

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

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

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.
}

Вы явно вызываете его в родительском классе еще до того, как создание дочернего класса будет завершено. Попробуйте self.somefield = self.get_name(), тогда cls будет TestClass.

jonrsharpe 27.08.2024 16:12

Вы продолжаете звонить TestParentClass.get_name(), что да, всегда будет TestParentClass. Вы просто вызываете его в области дочернего класса, но не вызываете метод дочернего класса.

deceze 27.08.2024 16:12
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
0
2
50
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 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)

Это работает удивительно для меня. Единственное, что следует отметить, это то, что моему линтеру (Pylint в VSCode) не нравится объявление somefield внутри __init_subclass__(), поскольку оно возвращает: Cannot assign to attribute "somefield" for class "type[TestParentClass]*" Attribute "somefield" is unknown. Тем не менее, он по-прежнему работает отлично. Я могу обойти это, установив type: ignore в этой строке или определив атрибут somefield = "" в качестве первой строки TestParentClass.

alelom 27.08.2024 16:59

Да, это своего рода проблема с Pylint (хотя я не виню его за то, что он не «выполнил» __init_subclass__, чтобы выяснить, какие атрибуты класса будут определены). Другое решение — добавить somefield: typing.ClassVar[str] в тело класса, хотя это отчасти подразумевает, что TestParentClass.somefield должен существовать или быть где-то определен, но TestParentClass.__init_subclass__ никогда не вызывается сам TestParentClass, а только его дочерние элементы.

chepner 27.08.2024 17:07

@alelom ты мог бы просто использовать somefield: typing.ClassVar[str]

juanpa.arrivillaga 27.08.2024 19:41

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

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).

Спасибо вам за это. Как вы заметили, ваше первое решение работает для print(TestClass().somefield), но для print(TestClass.somefield) возвращает что-то вроде <property object at 0x7ff93e6c2b10>. Второе решение работает для обоих, но я считаю его немного запутанным. Не могли бы вы рассказать об этом подробнее, возможно, объяснив, что означает noqa? Спасибо в любом случае.

alelom 27.08.2024 16:55

Эти комментарии # noqa предназначены для охлаждения IDE и не выполняют никакой другой функции, кроме этой. См. stackoverflow.com/a/45346791/8375783 Что касается classproperty, он делает то, что говорит имя, это свойство, которое заставляет декорированный метод взаимодействовать с cls (проектом класса), а не с self (экземпляром класса). ).

Trapli 27.08.2024 19:14

Другие вопросы по теме