class NiceClass():
some_value = SomeObject(...)
some_other_value = SomeOtherObject(...)
@classmethod
def get_all_vars(cls):
...
I want get_all_vars()
to return [SomeObject(...), SomeOtherObject(...)]
, or more specifically, the values of the variables in cls
.
Solutions tried that didn't work out for me:
return [cls.some_value, cls.some_other_value, ...]
(requires listing the variable manually)- subclassing
Enum
then usinglist(cls)
(requires usingsome_value.value
to access the value elsewhere in the program, also type hinting would be a mess) - namedtuples (nope not touching that subject, heard it was much more complicated than
Enum
) [value for key, value in vars(cls).items() if not callable(value) and not key.startswith("__")]
(too hacky due to usingvars(cls)
, also for some reason it also includesget_all_vars
due to it being aclassmethod
)
CodePudding user response:
There are two ways. This is a straight answer to your question:
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
This is what I suggest:
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)]
results:
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]
CodePudding user response:
You can create a list of values and define individual attributes at the same time with minimal boilerplate.
class NiceClass():
(some_value,
some_other_value,
) = _all_values = [
SomeObject(...),
SomeOtherObject(...)
]
@classmethod
def get_all_vars(cls):
return cls._all_values
The most obvious drawback to this is that it's easy to get the order of names and values out of sync.
Ideally, you might like to do something like
class NiceClass:
_attributes = {
'some_value': SomeObject(...),
'some_other_value': SomeOtherObject(...)
}
@classmethod
def get_all_vars(cls):
return cls._attributes.values()
and have some way to "inject" the contents of _attributes
into the class namespace directly. The simplest way to do this is with a mix-in class that defines __init_subclass__
:
class AddAttributes:
def __init_subclass__(cls, **kwargs):
super().__init_subclass__(**kwargs)
cls.__dict__.update(cls._attributes)
class NiceClass(AddAttributes):
# As above
...
CodePudding user response:
This might sound like a https://xyproblem.info/ but my solution might work in the other case as well. You can get the fields of an object by using __dict__
or vars
(which is considered more pythonic given: Python dictionary from an object's fields)
You could do something like:
class ClassTest:
def __init__(self):
self.one = 1
self.two = 2
list(vars(ClassTest()).values())
But you will see that it has some limitations. It doesn't recognize the not in self.variable_name
defined variables like you have used above. It might help you nonetheless, because you can simply define them in init.