Reputation: 470
How (in Python 3) to get values of all properties that belong to specific class. I need ONLY those values (properties) that defined in specific class without inherited ones.
Here is some example:
class A(object):
def __init__(self, color):
self._color = color
@property
def color(self):
return self._color
class B(A):
def __init__(self, color, height, width):
super().__init__(color)
self._height = height
self._width = width
@property
def height(self):
return self._height
@property
def width(self):
return self._width
and here is a code for fetching all values (including inherited):
b_inst = B('red', 10, 20)
val = [{p: b_inst.__getattribute__(p)} for p in dir(B)
if isinstance(getattr(B, p), property)]
print(val)
>> [{'color': 'red'}, {'height': 10}, {'width': 20}]
Now, I just want to retrieve values of properties defined ONLY in class B
, i.e. height
and width
.
Upvotes: 0
Views: 429
Reputation: 1186
Since, I'm gravitating towards this question only to find a useless answer, I worked one out:
from collections import defaultdict, deque
from typing import List, Dict
class A:
def __init__(self):
self.value_a1 = 1
self.value_a2 = 2
class B(A):
def __init__(self):
super().__init__()
self.value_b1 = 3
self.value_b2 = 4
class C(A):
def __init__(self):
super().__init__()
self.value_c1 = 5
self.value_c2 = 6
class D(B, C):
def __init__(self):
B.__init__(self)
C.__init__(self)
self.value_d1 = 7
self.value_d2 = 8
class E(D):
def __init__(self):
super().__init__()
self.value_e1 = 9
self.value_e2 = 10
def build_inheritance_graph(classes) -> Dict[object, List[object]]:
"""
:param classes:
:return:
"""
inheritance_graph = {}
# Recursive function to populate the graph for a class and its ancestors
def add_class_and_ancestors(cls):
if cls not in inheritance_graph:
inheritance_graph[cls] = list(cls.__bases__)
for parent in cls.__bases__:
add_class_and_ancestors(parent)
for cls in classes:
add_class_and_ancestors(cls)
return inheritance_graph
def topological_sort(inheritance_graph) -> List[object]:
"""
:param inheritance_graph:
:return:
"""
# Calculate in-degrees for each class
in_degree = defaultdict(int)
for cls, parents in inheritance_graph.items():
in_degree[cls] += 0 # Ensure each class is initialized in in_degree
for parent in parents:
in_degree[parent] += 0
in_degree[cls] += 1
# Collect classes with no incoming edges (in-degree of 0)
queue = deque([cls for cls, degree in in_degree.items() if degree == 0])
sorted_classes = []
while queue:
cls = queue.popleft()
sorted_classes.append(cls)
for child, parents in inheritance_graph.items():
if cls in parents: # Check if cls is a parent of child
in_degree[child] -= 1
if in_degree[child] == 0:
queue.append(child)
return sorted_classes
def get_declared_attributes(cls):
"""Return instance attributes defined directly in the given class's __init__."""
# Create a temporary instance
instance = cls()
# Capture all instance attributes
all_attrs = set(vars(instance).keys()) if hasattr(instance, '__dict__') else set()
# Check parent classes to remove inherited attributes
for base in cls.__bases__:
parent_instance = base()
parent_attrs = set(vars(parent_instance).keys()) if hasattr(parent_instance, '__dict__') else set()
all_attrs -= parent_attrs
return all_attrs
def get_mapping(classes):
"""
:param classes: list of class types
:return:
"""
graph = build_inheritance_graph(classes)
# Get classes sorted from least to most dependency
sorted_classes_ = topological_sort(graph)
props_by_class = list()
# Display the sorted classes
print("Classes sorted by dependency (from least to most):")
for cls_ in sorted_classes_:
props_list = get_declared_attributes(cls_)
parents_list = [p.__name__ for p in graph[cls_]]
# print(cls_.__name__, "->", parents_list, ": ", props_list)
props_by_class.append({
"class": cls_,
"parents": parents_list,
"properties": list(props_list)
})
return props_by_class
# Build the inheritance graph
mapping = get_mapping(classes=[B, C, D, E])
for item in mapping:
print("class:", item['class'].__name__, "\n\tparents:", item['parents'], "\n\tproperties:", item['properties'])
This prints:
class: object
parents: []
properties: []
class: A
parents: ['object']
properties: ['value_a2', 'value_a1']
class: B
parents: ['A']
properties: ['value_b1', 'value_b2']
class: C
parents: ['A']
properties: ['value_c1', 'value_c2']
class: D
parents: ['B', 'C']
properties: ['value_d2', 'value_d1']
class: E
parents: ['D']
properties: ['value_e1', 'value_e2']
Upvotes: 0
Reputation: 77942
Note that in Python "property" has a very specific meaning (the builtin property
type). If you're only concerned about this then you just have to lookup your child class's __dict__
:
val = [p.__get__(c) for k, p in type(c).__dict__.items() if isinstance(p, property)]
If you want something that works on any arbitrary attribute then what you ask for is just not possible, since Python objects (with a few exceptions) are dict-based (vs struct-based like in C++ or Java) and dynamic (any piece of code can add / remove arbitrary attributes on a per-instance basis) so there's no fix schema nor class-level definition of what attributes a given object may or not possess.
Upvotes: 1