Предположим, что мы имеем следующую иерархию классов:
class ClassA:
@property
def foo(self): return "hello"
class ClassB(ClassA):
@property
def bar(self): return "world"
Если я исследую __ dict __ в ClassB, я вижу только атрибут bar:
for name,_ in ClassB.__dict__.items():
if name.startswith("__"):
continue
print(name)
Выводится строка
Я могу использовать собственные средства для получения атрибутов не только указанного типа, но и его предков. Однако, мой вопрос заключается в том, есть ли у меня в Python способ сделать это без повторного создания колеса.
def return_attributes_including_inherited(type):
results = []
return_attributes_including_inherited_helper(type,results)
return results
def return_attributes_including_inherited_helper(type,attributes):
for name,attribute_as_object in type.__dict__.items():
if name.startswith("__"):
continue
attributes.append(name)
for base_type in type.__bases__:
return_attributes_including_inherited_helper(base_type,attributes)
Запуск моего кода следующим образом:
for attribute_name in return_attributes_including_inherited(ClassB):
print(attribute_name)
... возвращает оба бара и foo.
Обратите внимание, что я упрощаю некоторые вещи: коллизии имен, используя элементы(), когда для этого примера я мог бы использовать dict, пропуская все, что начинается с __, игнорируя возможность того, что у двух предков есть общий предок и т.д.
EDIT1 - Я попытался сделать пример простым. Но я действительно хочу как имя атрибута, так и ссылку на атрибут для каждого класса и класса предков. Один из приведенных ниже ответов дает мне лучший ответ, я отправлю код лучше, когда я его заработаю.
EDIT2 - Это делает то, что я хочу, и очень красноречиво. Это основано на ответе Эли ниже.
def get_attributes(type):
attributes = set(type.__dict__.items())
for type in type.__mro__:
attributes.update(type.__dict__.items())
return attributes
Он возвращает имена атрибутов и их ссылки.
EDIT3 - один из ответов, приведенных ниже, используется с помощью inspect.getmembers. Это кажется очень полезным, потому что он, как и dict, работает только на классах предков.
Поскольку большая часть того, что я пыталась сделать, это найти атрибуты, помеченные определенным дескриптором, и включить классы предков, вот какой-то код, который поможет сделать это, если он кому-то поможет:
class MyCustomDescriptor:
# This is greatly oversimplified
def __init__(self,foo,bar):
self._foo = foo
self._bar = bar
pass
def __call__(self,decorated_function):
return self
def __get__(self,instance,type):
if not instance:
return self
return 10
class ClassA:
@property
def foo(self): return "hello"
@MyCustomDescriptor(foo="a",bar="b")
def bar(self): pass
@MyCustomDescriptor(foo="c",bar="d")
def baz(self): pass
class ClassB(ClassA):
@property
def something_we_dont_care_about(self): return "world"
@MyCustomDescriptor(foo="e",bar="f")
def blah(self): pass
# This will get attributes on the specified type (class) that are of matching_attribute_type. It just returns the attributes themselves, not their names.
def get_attributes_of_matching_type(type,matching_attribute_type):
return_value = []
for member in inspect.getmembers(type):
member_name = member[0]
member_instance = member[1]
if isinstance(member_instance,matching_attribute_type):
return_value.append(member_instance)
return return_value
# This will return a dictionary of name & instance of attributes on type that are of matching_attribute_type (useful when you're looking for attributes marked with a particular descriptor)
def get_attribute_name_and_instance_of_matching_type(type,matching_attribute_type):
return_value = {}
for member in inspect.getmembers(ClassB):
member_name = member[0]
member_instance = member[1]
if isinstance(member_instance,matching_attribute_type):
return_value[member_name] = member_instance
return return_value