I need to code a SEARCH class, so that accessing its attributes by their nested representation(s).
Attributes and their relationship: the goal of the 'SEARCH' class is to obtain a serie of consecutive integer numbers 'n', selecting 1 or summing 2 of the predefined A, B, C, D constants, as follows:
A = 0 B = 1 C = 2 D = 4 with 'n' meeting these attributes relationship: n = (A or B) + (None or C or D), thus getting: n = 0..5
Expected Usage: the main constraint is to apply for nested attributes for acceding to the 'n' value series.
# Expected output 'n' from the 'SEARCH' class nested attributes:
print(SEARCH.A) # Output: 0
print(SEARCH.B) # Output: 1
print(SEARCH.A.C) # Output: 2 (= A + C = 0 + 2)
print(SEARCH.A.D) # Output: 4
print(SEARCH.B.C) # Output: 3
print(SEARCH.B.D) # Output: 5
print(SEARCH.A.A) # Output: to drop an error and break
print(SEARCH.B.B) # Output: to drop an error and break
I need this 'SEARCH' class to behave somehow similarly to an enumeration (See n=0..5). This is why I tried with Enum
and Flag
classes, as in my previous question on this same topic. The Enum
approach was wrong. I checked this nearest case question.
Code step 1: My first piece of code (after the Enum
tries), with some workarounds with regards to a real nested attribute sequence (C and D underscored) ; nevertheless, this first investigation already reflects what I am aiming:
class SEARCH:
# class attributes
A = 0
B = 1
C = 2
D = 4
class _A:
@staticmethod
def _C():
return SEARCH.A + SEARCH.C
@staticmethod
def _D():
return SEARCH.A + SEARCH.D
class _B:
@staticmethod
def _C():
return SEARCH.B + SEARCH.C
@staticmethod
def _D():
return SEARCH.B + SEARCH.D
def __getattr__(self, attr):
# Raise error for invalid nested attributes
if attr in ['A', 'B']:
raise AttributeError(f"Invalid nested attribute access: '{attr}'")
# Usage
try:
# Valid cases
print(SEARCH.A) # Expected Output: 0
print(SEARCH.B) # Expected Output: 1
print(SEARCH.C) # Expected Output: 2
print(SEARCH.D) # Expected Output: 4
print(SEARCH._A._C()) # Expected Output: 2 (0 + 2), but not using underscores
print(SEARCH._A._D()) # Expected Output: 4 (0 + 4)
print(SEARCH._B._C()) # Expected Output: 3 (1 + 2)
print(SEARCH._B._D()) # Expected Output: 5 (1 + 4)
# Invalid nested cases
try:
print(SEARCH.A.A()) # Should raise an error
except AttributeError as e:
print(f"Error: {e}")
try:
print(SEARCH.B.A()) # Should raise an error
except AttributeError as e:
print(f"Error: {e}")
except AttributeError as e:
print(f"General Error: {e}")
Code Step 2: my current investigations consist in using the __new__
method, for creating a new instance of a class, to be called before __init__
, where __new__
is used to run the initialize() class method automatically whenever the class is used. I am still struggling with a few errors, since the type object 'A' has no attribute 'value'. As follows:
class SEARCH:
A_value = 0
B_value = 1
C_value = 2
D_value = 4
class A:
pass
class B:
pass
def __new__(cls, *args, **kwargs):
cls.initialize() # Automatically initialize when the class is loaded
return super(SEARCH, cls).__new__(cls)
@classmethod
def initialize(cls):
# Dynamically set the nested class values after SEARCH is fully defined
cls.A.value = cls.A_value
cls.A.C = type('C', (), {'value': cls.A_value + cls.C_value})
cls.A.D = type('D', (), {'value': cls.A_value + cls.D_value})
cls.B.value = cls.B_value
cls.B.C = type('C', (), {'value': cls.B_value + cls.C_value})
cls.B.D = type('D', (), {'value': cls.B_value + cls.D_value})
# Testing
try:
# Valid cases
print(SEARCH.A.value) # Expected Output: 0
print(SEARCH.B.value) # Expected Output: 1
print(SEARCH.A.C.value) # Expected Output: 2 (0 + 2)
print(SEARCH.A.D.value) # Expected Output: 4 (0 + 4)
print(SEARCH.B.C.value) # Expected Output: 3 (1 + 2)
print(SEARCH.B.D.value) # Expected Output: 5 (1 + 4)
# Invalid nested cases
try:
print(SEARCH.A.A) # Should raise an error
except AttributeError as e:
print(f"Error: {e}")
try:
print(SEARCH.B.A) # Should raise an error
except AttributeError as e:
print(f"Error: {e}")
except AttributeError as e:
print(f"General Error: {e}")
I could not find yet what I'm doing wrong .
Any hints or direction about: how to get a sum value from nested attributes in Python-3.x ?
I found a better way applying for metaclass
, also more readable as follows :
class SEARCHMeta(type):
def __init__(cls, name, bases, dct):
super().__init__(name, bases, dct)
# Dynamically set the nested class values after SEARCH is fully defined
if name == "SEARCH":
cls.A.value = cls.A_value
cls.A.C = type('C', (), {'value': cls.A_value + cls.C_value})
cls.A.D = type('D', (), {'value': cls.A_value + cls.D_value})
cls.B.value = cls.B_value
cls.B.C = type('C', (), {'value': cls.B_value + cls.C_value})
cls.B.D = type('D', (), {'value': cls.B_value + cls.D_value})
class SEARCH(metaclass=SEARCHMeta):
A_value = 0
B_value = 1
C_value = 2
D_value = 4
class A:
pass
class B:
pass
# Usage
try:
# Valid cases
print(SEARCH.A.value) # Expected Output: 0
print(SEARCH.B.value) # Expected Output: 1
print(SEARCH.A.C.value) # Expected Output: 2 (0 + 2)
print(SEARCH.A.D.value) # Expected Output: 4 (0 + 4)
print(SEARCH.B.C.value) # Expected Output: 3 (1 + 2)
print(SEARCH.B.D.value) # Expected Output: 5 (1 + 4)
# Invalid nested cases
try:
print(SEARCH.A.A) # Should raise an error
except AttributeError as e:
print(f"Error: {e}")
try:
print(SEARCH.B.A) # Should raise an error
except AttributeError as e:
print(f"Error: {e}")
except AttributeError as e:
print(f"General Error: {e}")