Skip to content

Commit 07c611f

Browse files
committed
enum.py: Make __iter__, _update, _scan_class_attrs, list a classmethods.
igned-off-by: Ihor Nehrutsa <Ihor.Nehrutsa@gmail.com>
1 parent 3e10d5b commit 07c611f

3 files changed

Lines changed: 61 additions & 35 deletions

File tree

python-stdlib/enum/enum.py

Lines changed: 57 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# enum.py
2-
# version="1.2.3"
2+
# version="1.2.4"
33

44

55
class EnumValue:
@@ -11,9 +11,6 @@ def __init__(self, value, name):
1111
def __repr__(self):
1212
return f"{self.name}: {self.value}"
1313

14-
# def __str__(self):
15-
# return str(self.value)
16-
1714
def __call__(self):
1815
return self.value
1916

@@ -22,9 +19,6 @@ def __eq__(self, other):
2219
return self.value == other.value
2320
return self.value == other
2421

25-
# def __int__(self):
26-
# return self.value
27-
2822
def __setattr__(self, key, value):
2923
raise AttributeError("EnumValue is immutable")
3024

@@ -40,6 +34,9 @@ def __new__(cls, name=None, names=None):
4034
return super(Enum, cls).__new__(cls)
4135

4236
def __init__(self, name=None, names=None):
37+
if hasattr(self, '_initialized'):
38+
return
39+
4340
# 1. Convert class-level attributes (constants) to EnumValue objects
4441
self._scan_class_attrs()
4542

@@ -64,29 +61,49 @@ def _lookup(cls, value):
6461
if not callable(attr) and attr == value:
6562
# Wrap static numbers found in class definition
6663
return EnumValue(attr, key)
67-
6864
raise AttributeError(f"{value} is not in {cls.__name__}")
6965

70-
def list(self):
66+
@classmethod
67+
def __iter__(cls):
68+
if '_initialized' not in cls.__dict__:
69+
cls._scan_class_attrs()
70+
setattr(cls, '_initialized', True)
71+
72+
for key in dir(cls):
73+
if key.startswith('_'):
74+
continue
75+
attr = getattr(cls, key)
76+
if isinstance(attr, EnumValue):
77+
yield attr
78+
79+
@classmethod
80+
def list(cls):
81+
if '_initialized' not in cls.__dict__:
82+
cls._scan_class_attrs()
83+
setattr(cls, '_initialized', True)
84+
7185
# Returns a list of all members
72-
return [member for member in self]
86+
return [getattr(cls, key) for key in dir(cls)
87+
if isinstance(getattr(cls, key), EnumValue)]
7388

74-
def _update(self, key, value):
75-
setattr(self.__class__, key, EnumValue(value, key))
89+
@classmethod
90+
def _update(cls, key, value):
91+
setattr(cls, key, EnumValue(value, key))
7692

77-
def _scan_class_attrs(self):
93+
@classmethod
94+
def _scan_class_attrs(cls):
7895
# Converts static class attributes into EnumValue objects
7996
# List of methods and internal names that should not be converted
8097
ignored = ('is_value', 'list')
81-
for key in dir(self.__class__):
98+
for key in dir(cls):
8299
# Skip internal names and methods
83100
if key.startswith('_') or key in ignored:
84101
continue
85102

86-
value = getattr(self.__class__, key)
103+
value = getattr(cls, key)
87104
# Convert only constants, not methods
88105
if not callable(value) and not isinstance(value, EnumValue):
89-
self._update(key, value)
106+
cls._update(key, value)
90107

91108
def is_value(self, value):
92109
return any(member.value == value for member in self)
@@ -100,8 +117,12 @@ def __repr__(self):
100117
return f"{self.__class__.__name__}(names={members})"
101118

102119
def __call__(self, value):
120+
if not hasattr(self, '_initialized'):
121+
self._scan_class_attrs()
122+
object.__setattr__(self, '_initialized', True)
123+
103124
for member in self:
104-
if member.value == value:
125+
if member.value == value or member.name == value:
105126
return member
106127
raise AttributeError(f"{value} is not in {self.__class__.__name__}")
107128

@@ -118,12 +139,6 @@ def __delattr__(self, key):
118139
def __len__(self):
119140
return sum(1 for _ in self)
120141

121-
def __iter__(self):
122-
for key in dir(self.__class__):
123-
attr = getattr(self.__class__, key)
124-
if isinstance(attr, EnumValue):
125-
yield attr
126-
127142
def __eq__(self, other):
128143
if not isinstance(other, Enum):
129144
return False
@@ -134,28 +149,36 @@ def __eq__(self, other):
134149
# --- Usage Example 1 ---
135150
# Standard Class Definition
136151
class Color(Enum):
137-
RED = 'red'
138-
GREEN = 'green'
139-
152+
RED = 1
153+
GREEN = 2
154+
BLUE = 3
155+
156+
print("Color.list():", Color.list())
157+
print("Color().list():", Color().list())
158+
159+
# Iteration
160+
print("Members list:", [member for member in Color()])
161+
print("Names list:", [member.name for member in Color()])
162+
print("Values list:", [member.value for member in Color()])
163+
140164
# Create instance
141165
c = Color()
142166
print(f"Enum c: {c}")
143-
print("c.list():", c.list())
144167

145168
# Basic access
146169
print(f"RED: Name={c.RED.name}, Value={c.RED.value}, EnumValue={c.RED}, Call={c.RED()} ")
147170

148171
# Assertions
149172
assert c.RED.name == 'RED'
150-
assert c.RED.value == 'red'
151-
assert c.RED == 'red'
152-
assert c.RED() == 'red'
173+
assert c.RED.value == 1
174+
assert c.RED == 1
175+
assert c.RED() == 1
153176

154177
# Reverse Lookup via instance call
155-
print(f"c('red') lookup object: {c('red')}, Name={c('red').name}, value={c('red').value}") # RED
156-
assert c('red').name == 'RED'
157-
assert c('red').value == 'red'
158-
assert c('red') == 'red'
178+
#print(f"c(1) lookup object: {c(1)}, Name={c(1).name}, value={c(1).value}") # RED
179+
assert c(1).name == 'RED'
180+
assert c(1).value == 1
181+
assert c(1) == 1
159182

160183
# Iteration
161184
print("Values list:", [member.value for member in c])

python-stdlib/enum/test_enum.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,9 @@ def test_call_reverse_lookup(self):
3636
result = self.color(1)
3737
self.assertEqual(result.name, 'RED')
3838
self.assertEqual(result.value, 1)
39+
result = self.color('RED')
40+
self.assertEqual(result.name, 'RED')
41+
self.assertEqual(result.value, 1)
3942

4043
# Перевірка виключення для неіснуючого значення
4144
with self.assertRaises(AttributeError):

python-stdlib/enum/test_enum_.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ def test_len_and_list(self):
104104
"""Test utility functions like __len__ and list."""
105105
self.assertEqual(len(self.color_inst), 3)
106106
members_list = self.color_inst.list()
107-
self.assertEqual(members_list, [self.color_inst.BLUE, self.color_inst.GREEN, self.color_inst.RED])
107+
self.assertEqual(members_list, [self.color_inst.RED, self.color_inst.GREEN, self.color_inst.BLUE])
108108

109109
def test_deletion_prevention(self):
110110
"""Verify that members cannot be deleted."""

0 commit comments

Comments
 (0)