class NiceClass():
some_value = SomeObject(...)
some_other_value = SomeOtherObject(...)
@classmethod
def get_all_vars(cls):
...
Я хочу, чтобы get_all_vars()
возвращал [SomeObject(...), SomeOtherObject(...)]
, а точнее, значения переменных в cls
.
Пробованные решения, которые не сработали для меня:
return [cls.some_value, cls.some_other_value, ...]
(требуется указание переменной вручную)Enum
, а затем использование list(cls)
(требуется использование some_value.value
для доступа к значению в другом месте программы, также подсказка типа будет беспорядком)Enum
)[value for key, value in vars(cls).items() if not callable(value) and not key.startswith("__")]
(слишком хакерский из-за использования vars(cls)
, также по какой-то причине он также включает get_all_vars
из-за того, что это classmethod
)Использование isinstance(value, classmethod)
вместо callable(value)
удалит get_all_vars
из списка. Тем не менее, это не станет менее хакерским.
Это может звучать как https://xyproblem.info/, но мое решение может работать и в другом случае. Вы можете получить поля объекта, используя __dict__
или vars
(что считается более питоническим: Словарь Python из полей объекта)
Вы можете сделать что-то вроде:
class ClassTest:
def __init__(self):
self.one = 1
self.two = 2
list(vars(ClassTest()).values())
Но вы увидите, что у него есть некоторые ограничения. Он не распознает не определенные в self.variable_name
переменные, как вы использовали выше. Тем не менее, это может вам помочь, потому что вы можете просто определить их в init.
OP уже знает о vars
как о возможном решении, и в любом случае этот пример не отражает вариант использования OP.
Вы правы, но я все еще не уверен, почему это слишком хакерски. Поэтому я подумал, что это может помочь привести пример.
@ Натан, это совсем не хакерство; Вот часть vars
документации: With an argument, equivalent to object.__dict__
, что означает, что это именно то его назначение.
@CommissarVasiliKarlovic Я согласен. Спрашивающий не знает.
Есть два способа. Это прямой ответ на ваш вопрос:
class Foo:
pass
class Bar:
x: int = 1
y: str = 'hello'
z: Foo = Foo()
@classmethod
def get_all(cls):
xs = []
for name, value in vars(cls).items():
if not (name.startswith('__') or isinstance(value, classmethod)):
xs.append(value)
return xs
Вот что я предлагаю:
from dataclasses import dataclass, fields
class Foo:
pass
@dataclass
class Bar:
x: int = 1
y: str = 'hello'
z: Foo = Foo()
@classmethod
def get_defaults(cls):
return [f.default for f in fields(cls)]
@classmethod
def get_all(cls):
return [getattr(cls, f.name) for f in fields(cls)]
полученные результаты:
Bar.get_defaults() == Bar.get_all()
# True -> [1, 'hello', __main__.Foo]
Bar.x = 10
Bar.get_defaults() == Bar.get_all()
# False -> [1, 'hello', __main__.Foo] != [10, 'hello', __main__.Foo]
метод dataclass работает, первый тоже немного работает, хотя он не проверяет вызываемые объекты
@TNTzx Я отредактировал dataclass
. Пожалуйста, дайте ему чек
понял, работает
Вы можете создать список значений и, определить отдельные атрибуты одновременно с минимальным шаблоном.
class NiceClass():
(some_value,
some_other_value,
) = _all_values = [
SomeObject(...),
SomeOtherObject(...)
]
@classmethod
def get_all_vars(cls):
return cls._all_values
Наиболее очевидным недостатком этого является то, что порядок имен и значений может легко рассинхронизироваться.
В идеале вы могли бы сделать что-то вроде
class NiceClass:
_attributes = {
'some_value': SomeObject(...),
'some_other_value': SomeOtherObject(...)
}
@classmethod
def get_all_vars(cls):
return cls._attributes.values()
и иметь способ «внедрить» содержимое _attributes
напрямую в пространство имен класса. Самый простой способ сделать это — смешанный класс, который определяет __init_subclass__
:
class AddAttributes:
def __init_subclass__(cls, **kwargs):
super().__init_subclass__(**kwargs)
cls.__dict__.update(cls._attributes)
class NiceClass(AddAttributes):
# As above
...
@ThierryLathuille не тот ответ, который я искал, но это решение моей проблемы, так что? все же хотелось бы решения этого вопроса