-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Expand file tree
/
Copy pathenum.py
More file actions
162 lines (125 loc) · 5.93 KB
/
enum.py
File metadata and controls
162 lines (125 loc) · 5.93 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
# enum.py
# version="1.2.0"
_Err = "no such attribute: "
def enum(**kwargs): # `**kwargs` kept backwards compatible as in the Internet examples
return Enum(**kwargs)
class EnumValue:
def __init__(self, value, name):
object.__setattr__(self, 'value', value)
object.__setattr__(self, 'name', name)
def __repr__(self):
return str(self.value)
def __call__(self):
return self.value
def __eq__(self, other):
return self.value == (other.value if isinstance(other, EnumValue) else other)
def __setattr__(self, key, value):
raise AttributeError("EnumValue is immutable")
class Enum:
def __init__(self, **kwargs):
"""Ініціалізація Enum. Динамічні аргументи видалено."""
# 1. Collect class-level attributes (constants)
self._scan_class_attrs()
# 2. Add arguments from the constructor
if kwargs:
self.append(**kwargs)
def _update(self, key, value):
setattr(self.__class__, key, EnumValue(value, key))
def _scan_class_attrs(self):
"""Перетворює статичні атрибути класу в об'єкти EnumValue"""
# Список методів та службових імен, які не треба перетворювати
ignored = ('is_value', 'key_from_value')
for key in dir(self.__class__):
# Пропускаємо внутрішні імена та методи
if key.startswith('_') or key in ignored:
continue
value = getattr(self.__class__, key)
# Перетворюємо лише константи, не методи
if not callable(value):
self._update(key, value)
# Опціонально: видаляємо з класу оригінальне значення, щоб звільнити RAM
# try: delattr(self.__class__, key)
# except: pass
# def append(self, **kwargs):
# for key, value in kwargs.items():
# self._update(key, value)
#
# return self
def append(self, **kwargs):
forbidden = ('is_value', 'append', '_update', '_scan_class_attrs')
for key, value in kwargs.items():
if key not in forbidden:
self._update(key, value)
return self
def __repr__(self):
"""Спрощений repr, що показує лише назви констант"""
keys = [k for k in dir(self.__class__) if not k.startswith('_')]
return f"<{type(self).__name__} Enum: {keys}>"
def is_value(self, value):
"""Перевіряє, чи існує EnumValue з таким *значенням*"""
# Ітеруємо по атрибутах класу
for key in dir(self.__class__):
attr = getattr(self.__class__, key)
if isinstance(attr, EnumValue) and attr.value == value:
return True
return False
def __call__(self, value):
"""Зворотний пошук об'єкта Enum за значенням: Color(1) -> RED"""
# Ітеруємо по атрибутах класу
for key in dir(self.__class__):
attr = getattr(self.__class__, key)
if isinstance(attr, EnumValue) and attr.value == value:
# Повертаємо сам об'єкт (RED, GREEN...), щоб працював .name
return attr
raise ValueError(_Err + str(value))
def __delattr__(self, key):
"""Забороняємо видалення елементів Enum"""
raise AttributeError("Enum members cannot be deleted")
def __len__(self):
"""Повертає кількість елементів в Enum"""
count = 0
for key in dir(self.__class__):
if not key.startswith('_') and isinstance(getattr(self.__class__, key), EnumValue):
count += 1
return count
def __iter__(self):
"""Робить Enum ітерабельним. Повертає об'єкти EnumValue."""
# Ми шукаємо всі атрибути класу, які є EnumValue
for key in dir(self.__class__):
if key.startswith('_'):
continue
attr = getattr(self.__class__, key)
if isinstance(attr, EnumValue):
# Повертаємо сам об'єкт
yield attr
# --- Приклад використання ---
if __name__ == '__main__':
# Визначення Enum (тільки статично)
class Color(Enum):
RED = 1
GREEN = 2
BLUE = 3
# Створюємо екземпляр
c = Color()
# Спроба створити з аргументами (тепер ігнорується або викликає TypeError,
# якщо перевизначити __init__ зі строгим контролем)
# try:
# c_bad = Color('BLACK')
# except TypeError as e:
# print(f"Init args blocked: {e}")
print('c instance repr:', c)
print(dir(c))
# Базовий доступ
print(f"RED: Name={c.RED.name}, Value={c.RED.value}")
# Твердження (assertions)
assert c.RED == 1
assert c.RED.value == 1
assert c.RED.name == 'RED'
# Зворотний пошук (тепер повертає об'єкт)
print(f"c(1) lookup object: {c(1)}, Name={c(1).name}")
assert c(1).name == 'RED'
assert c(1) == 1
# Ітерація
print("Values list:", [member.value for member in c])
print("Names list:", [member.name for member in c])
print("Members:", [member for member in c])