Я хотел бы создать метод класса или абстрактный метод в родительском классе. Этот метод должен возвращать имя класса реализации.
Например, используя метод класса:
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.
}
Вы продолжаете звонить TestParentClass.get_name()
, что да, всегда будет TestParentClass
. Вы просто вызываете его в области дочернего класса, но не вызываете метод дочернего класса.
Как указано в комментариях, решение требует, чтобы экземпляр дочернего класса создавался при вызове метода.
Способ сделать это — использовать метод класса в родительском классе, а также реализовать в родительском классе конструктор, который устанавливает поле. Например:
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
.
Да, это своего рода проблема с Pylint (хотя я не виню его за то, что он не «выполнил» __init_subclass__
, чтобы выяснить, какие атрибуты класса будут определены). Другое решение — добавить somefield: typing.ClassVar[str]
в тело класса, хотя это отчасти подразумевает, что TestParentClass.somefield
должен существовать или быть где-то определен, но TestParentClass.__init_subclass__
никогда не вызывается сам TestParentClass
, а только его дочерние элементы.
@alelom ты мог бы просто использовать somefield: typing.ClassVar[str]
Почему бы вам не использовать свойство вместо метода?
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
? Спасибо в любом случае.
Эти комментарии # noqa
предназначены для охлаждения IDE и не выполняют никакой другой функции, кроме этой. См. stackoverflow.com/a/45346791/8375783 Что касается classproperty
, он делает то, что говорит имя, это свойство, которое заставляет декорированный метод взаимодействовать с cls
(проектом класса), а не с self
(экземпляром класса). ).
Вы явно вызываете его в родительском классе еще до того, как создание дочернего класса будет завершено. Попробуйте
self.somefield = self.get_name()
, тогдаcls
будетTestClass
.