Python: Почему copy.deepcopy() завершается с ошибкой: «Объект <class> не имеет атрибута '...'», даже если атрибут сущестPython

Программы на Python
Ответить Пред. темаСлед. тема
Anonymous
 Python: Почему copy.deepcopy() завершается с ошибкой: «Объект <class> не имеет атрибута '...'», даже если атрибут сущест

Сообщение Anonymous »

Я изо всех сил пытаюсь найти основную причину, и поэтому мне трудно создать более минимальное воспроизведение ошибки. Из-за размера я опубликую код внизу.
Я создал несколько объектов пользовательских классов и инициализировал каждый из них в функции, которая была разработана для их переплетения для эффективной навигации. по коду, необходимому для интерфейсных систем.
Я обнаружил, что попытка глубокого копирования набора пробелов завершается неудачей, а попытка глубокого копирования набора задач — нет. Меня это довольно сбивает с толку, поскольку они используют схожие подходы к реализации пользовательских методов __hash__ и __eq__ (что, как я предполагаю, вероятно, связано с проблемой).
Когда при попытке глубокого копирования (пространства) я получаю AttributeError: объект «Space» не имеет атрибута «_id», хотя объект Space инициализируется с этим атрибутом. Задача также использует этот атрибут в своем методе __hash__, но по какой-то причине deepcopy успешно обрабатывает коллекции задач. Мне очень трудно понять, почему это не работает с коллекцией Space и работает с коллекцией объектов Task.
Я тоже пытался при аудите свойств классов __dict__ во время выполнения все выглядит нормально.
"Минимальное" воспроизведение:
from copy import deepcopy

# For performal natural "human" sorting
def natural_keys(text):
# Usage example:
# result = ['A 1', 'A 2', 'A 22', 'A 3']
# sorted(result, key=natural_keys)
import re
def atoi(text):
return int(text) if text.isdigit() else text

if hasattr(text, '__repr__'):
text = text.__repr__()
return [atoi(c) for c in re.split(r'(\d+)', text)]

class Task:
def __init__(self, id, name, ordinal_position, description=None,
float_forward=0, float_backward=0):
self._name = name
self._id = id
self._spaces = set()
self._ordinal_pos = ordinal_position
self._description = description
self._float_forward = float_forward
self._float_backward = float_backward
self._pretty_name = f"{self.ordinal_pos}. {self.name}"

@property
def name(self):
return self._name

@property
def id(self):
return self._id

@property
def ordinal_pos(self):
return self._ordinal_pos

@property
def description(self):
return self._description

@property
def float_forward(self):
return self._float_forward

@property
def float_backward(self):
return self._float_backward

@property
def pretty_name(self):
return self._pretty_name

def __hash__(self):
return hash((self.id, self.name))

def __eq__(self, other):
if not isinstance(other, Task):
return False
return (
self._id == other.id
and self.name == other.name
)

def __repr__(self):
return self.pretty_name

class TaskSet:
def __init__(self):
self._tasks = set()

@property
def raw(self):
return self._tasks

@property
def pretty(self):
return sorted(self._tasks, key=natural_keys)

def add(self, task, safe=False):
if task in self._tasks and not safe:
raise ValueError(f'Task already exists: {task}')
if not isinstance(task, Task):
raise TypeError("Can only add Task objects.")
self._tasks.add(task)

def update(self, tasks):
if not all([isinstance(task, Task) for task in tasks]):
raise TypeError("Can only add Task objects.")
self._tasks.update(tasks)

def drop(self, identifier, safe=False):
task = self.__getitem__(identifier, safe=safe)
if task:
self._tasks.remove(task)
return task

def __getitem__(self, identifier, safe=False):
if not identifier:
raise ValueError(f'identifier of "{identifier}" is invalid.')
elif isinstance(identifier, Task):
for task in self._tasks:
if task == identifier:
return task
elif isinstance(identifier, str):
for task in self._tasks:
if task.__repr__() == identifier.__repr__():
return task

if not safe:
raise KeyError(f'No task found with identifier: "{identifier}".')

def __iter__(self):
return iter(self._tasks)

def __repr__(self):
return f"TaskSet({self.pretty})"

class Space:
def __init__(self, id, workcenter_id, name, building, section, comment=None, **kwargs):
self._id = id
self._workcenter_id = workcenter_id
self._name = name
self._building = building
self._section = section
self._comment = comment
self._taskset = TaskSet()
self._workcenter = None
self._pretty_name = f"{self._name}; {self._building}/{self._section}"

@property
def id(self):
return self._id

@property
def workcenter_id(self):
return self._workcenter_id

@property
def name(self):
return self._name

@property
def building(self):
return self._building

@property
def section(self):
return self._section

@property
def comment(self):
return self._comment

@property
def pretty_name(self):
return self._pretty_name

@property
def tasks(self):
return self._taskset

@property
def pretty_tasks(self):
return self._taskset.pretty

@property
def workcenter(self):
return self._workcenter

def __repr__(self):
return self._name

def __hash__(self):
return hash((self._id, self._name, self._building, self._section))

def __eq__(self, other):
if not isinstance(other, Space):
return False
return (
self._id == other._id
and self._name == other._name
and self._building == other._building
and self._section == other._section
)

def __lt__(self, other):
return self._name < other._name

class Workcenter:
def __init__(self, id, name, building, section, **kwargs):
self._id = id
self._name = name
self._building = building
self._section = section
self._spaces = set()
self._taskset = TaskSet()

@property
def id(self):
return self._id

@property
def name(self):
return self._name

@property
def building(self):
return self._building

@property
def section(self):
return self._section

@property
def comment(self):
return self._comment

@property
def pretty_name(self):
if not self._pretty_name:
self._pretty_name = f"{self.spaces}; {self._building}/{self._section}"
return self._pretty_name

@property
def spaces(self):
return self._spaces

@property
def pretty_spaces(self):
return sorted(self._spaces, key=natural_keys)

@property
def tasks(self):
return self._taskset

@property
def pretty_tasks(self):
return self._taskset.pretty

def add(self, space):
if not isinstance(space, Space):
raise TypeError("Can only add Space objects.")
if space in self._spaces:
raise ValueError(f'Space already exists: {space}')
self._spaces.add(space)

def get_spaces_by_task(self, task):
return [space for space in self.pretty_spaces if task in space.tasks]

def drop(self, identifier, safe=False):
space = self.__getitem__(identifier, safe=safe)
if space:
self._spaces.remove(space)
return space

def __getitem__(self, identifier, safe=False):
if not identifier:
raise ValueError(f'identifier of "{identifier}" is invalid.')
elif isinstance(identifier, Space):
for space in self._spaces:
if space == identifier:
return space
elif isinstance(identifier, str):
for space in self._spaces:
if space.__repr__() == identifier.__repr__():
return space

if not safe:
raise KeyError(
f'No space found with identifier: "{identifier}" '
f'of type {type(identifier)}.'
)

def __iter__(self):
return iter(self._spaces)

def __repr__(self):
return self._name

def __hash__(self):
return hash(self._id)

def __eq__(self, other):
if not isinstance(other, Workcenter):
return False
return self._id == other._id

class WorkcenterSet:
def __init__(self):
self._workcenters = set()
self._spaces = set()
self._taskset = TaskSet()
self._workcenters_by_id = {}
self._workcenters_by_name = {}

@property
def workcenters(self):
return sorted(self._workcenters, key=lambda x: (x.building, x.section, x.spaces))

@property
def spaces(self):
return self._spaces

@property
def pretty_spaces(self):
return sorted(self._spaces, key=natural_keys)

@property
def tasks(self):
return self._taskset

@property
def pretty_tasks(self):
return self._taskset.pretty

def add(self, workcenter):
if workcenter in self._workcenters:
raise ValueError(f'Workcenter already exists: {task}')
if not isinstance(workcenter, Workcenter):
raise TypeError("Can only add Workcenter objects.")
self._workcenters.add(workcenter)

def filter(self, buildings: list[str] = [], sections: list[str] = []):
filtered_workcenters = WorkcenterSet()

for workcenter in self._workcenters:
add_workcenter = True
if buildings and workcenter.building not in buildings:
add_workcenter = False
if sections and workcenter.section not in sections:
add_workcenter = False
if add_workcenter:
filtered_workcenters.add(workcenter)
filtered_workcenters._spaces.update(workcenter._spaces)
filtered_workcenters._taskset.update(workcenter._taskset)

return filtered_workcenters

def get(self, identifier, default=None):
try:
return self.__getitem__(identifier)
except KeyError:
return default

def __getitem__(self, identifier):
# https://github.com/streamlit/streamlit/ ... 1570958563
if str(type(identifier)) == str(Workcenter):
for workcenter in self._workcenters:
if workcenter.id == identifier.id:
return workcenter
else:
for workcenter in self._workcenters:
if identifier == workcenter._id or identifier == workcenter._name:
return workcenter

raise KeyError(f"No workcenter found with identifier: {identifier}")

def __delitem__(self, identifier):
workcenter = self.__getitem__(identifier)
del self._workcenters[workcenter]

def __iter__(self):
return iter(self._workcenters)

def __repr__(self):
return f"WorkcenterSet({self.workcenters})"

def model_workcenterset(detailed_workcenters_raw, detailed_spaces_raw, space_tasks_raw, tasks_raw):
from copy import deepcopy
from collections import defaultdict

def discover_spaces_workcenter(workcenterset):
for workcenter in workcenterset:
for space in workcenter._spaces:
space._workcenter = workcenter

def discover_workcenter_taskset(workcenterset):
for workcenter in workcenterset:
taskset = TaskSet()
for space in workcenter.spaces:
for task in space.tasks:
taskset.add(task, safe=True)
workcenter._taskset = taskset

def discover_workcenterset_taskset(workcenterset):
taskset = TaskSet()
for workcenter in workcenterset:
for task in workcenter.tasks:
taskset.add(task, safe=True)
workcenterset._taskset = taskset

def discover_workcenterset_spaces(workcenterset):
spaces = set()
for workcenter in workcenterset:
for space in workcenter.spaces:
spaces.add(space)
workcenterset._spaces = spaces

# Reference for look-up tasks by space id
space_tasks = defaultdict(list)
for space_task in space_tasks_raw:
space_id = space_task['space_id']
task_id = space_task['task_id']
space_tasks[space_id].append(task_id)

# Reference for look-up: space by space id; task by task id
detailed_spaces = {space.pop('id'):space for space in deepcopy(detailed_spaces_raw)}
tasks = {task.pop('id'):task for task in deepcopy(tasks_raw)}

# Filling Workcenters and spaces
workcenterset = WorkcenterSet()
for _workcenter in detailed_workcenters_raw:
workcenter = Workcenter(**_workcenter)
for _id in _workcenter['space_ids']:
space = Space(id=_id, **detailed_spaces[_id])
for task_id in space_tasks[_id]:
task = Task(id=task_id, **tasks[task_id])
space.tasks.add(task)

workcenter.add(space)
workcenterset.add(workcenter)

# Adds reference (in-place) throughout objects for easy navigation
discover_spaces_workcenter(workcenterset)
discover_workcenter_taskset(workcenterset)
discover_workcenterset_taskset(workcenterset)
discover_workcenterset_spaces(workcenterset)
return workcenterset

data = {
'detailed_workcenters_raw': [
{
'id': 116,
'name': 'B2/S2; 4th Floor Corridor',
'building': 'BUILDING 2',
'section': 'SECTION 2',
'space_ids': [317],
'space_names': ['4th Floor Corridor']
},
{
'id': 87,
'name': 'B2/S1; 4058, 4072, 4073, 4074',
'building': 'BUILDING 2',
'section': 'SECTION 1',
'space_ids': [241, 242, 243, 244],
'space_names': ['4058', '4072', '4073', '4074']
}
],
'detailed_spaces_raw': [
{
'id': 241,
'workcenter_id': 87,
'name': '4058',
'building': 'BUILDING 2',
'section': 'SECTION 1'
},
{
'id': 242,
'workcenter_id': 87,
'name': '4072',
'building': 'BUILDING 2',
'section': 'SECTION 1'
},
{
'id': 243,
'workcenter_id': 87,
'name': '4073',
'building': 'BUILDING 2',
'section': 'SECTION 1'
},
{
'id': 244,
'workcenter_id': 87,
'name': '4074',
'building': 'BUILDING 2',
'section': 'SECTION 1'
},
{
'id': 317,
'workcenter_id': 116,
'name': '4th Floor Corridor',
'building': 'BUILDING 2',
'section': 'SECTION 2'
}
],
'space_tasks_raw': [
{'id': 242, 'space_id': 241, 'task_id': 1},
{'id': 243, 'space_id': 242, 'task_id': 1},
{'id': 244, 'space_id': 243, 'task_id': 1},
{'id': 245, 'space_id': 244, 'task_id': 1},
{'id': 318, 'space_id': 317, 'task_id': 1}
],
'tasks_raw': [
{
'id': 1,
'ordinal_position': 1,
'name': 'shim and shave / frame pick up'
}
]
}

wcs = model_workcenterset(**data)

deepcopy(wcs.tasks) ## Passed
deepcopy(wcs.spaces) ## Fails

Вот полная обратная трассировка:
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
Cell In[75], line 513
510 wcs = model_workcenterset(**data)
512 deepcopy(wcs.tasks) ## Passed
--> 513 deepcopy(wcs.spaces) ## Fails

File ~\AppData\Local\Programs\Python\Python311\Lib\copy.py:172, in deepcopy(x, memo, _nil)
170 y = x
171 else:
--> 172 y = _reconstruct(x, memo, *rv)
174 # If is its own copy, don't memoize.
175 if y is not x:

File ~\AppData\Local\Programs\Python\Python311\Lib\copy.py:265, in _reconstruct(x, memo, func, args, state, listiter, dictiter, deepcopy)
263 if deep and args:
264 args = (deepcopy(arg, memo) for arg in args)
--> 265 y = func(*args)
266 if deep:
267 memo[id(x)] = y

File ~\AppData\Local\Programs\Python\Python311\Lib\copy.py:264, in (.0)
262 deep = memo is not None
263 if deep and args:
--> 264 args = (deepcopy(arg, memo) for arg in args)
265 y = func(*args)
266 if deep:

File ~\AppData\Local\Programs\Python\Python311\Lib\copy.py:146, in deepcopy(x, memo, _nil)
144 copier = _deepcopy_dispatch.get(cls)
145 if copier is not None:
--> 146 y = copier(x, memo)
147 else:
148 if issubclass(cls, type):

File ~\AppData\Local\Programs\Python\Python311\Lib\copy.py:206, in _deepcopy_list(x, memo, deepcopy)
204 append = y.append
205 for a in x:
--> 206 append(deepcopy(a, memo))
207 return y

File ~\AppData\Local\Programs\Python\Python311\Lib\copy.py:172, in deepcopy(x, memo, _nil)
170 y = x
171 else:
--> 172 y = _reconstruct(x, memo, *rv)
174 # If is its own copy, don't memoize.
175 if y is not x:

File ~\AppData\Local\Programs\Python\Python311\Lib\copy.py:271, in _reconstruct(x, memo, func, args, state, listiter, dictiter, deepcopy)
269 if state is not None:
270 if deep:
--> 271 state = deepcopy(state, memo)
272 if hasattr(y, '__setstate__'):
273 y.__setstate__(state)

File ~\AppData\Local\Programs\Python\Python311\Lib\copy.py:146, in deepcopy(x, memo, _nil)
144 copier = _deepcopy_dispatch.get(cls)
145 if copier is not None:
--> 146 y = copier(x, memo)
147 else:
148 if issubclass(cls, type):

File ~\AppData\Local\Programs\Python\Python311\Lib\copy.py:231, in _deepcopy_dict(x, memo, deepcopy)
229 memo[id(x)] = y
230 for key, value in x.items():
--> 231 y[deepcopy(key, memo)] = deepcopy(value, memo)
232 return y

File ~\AppData\Local\Programs\Python\Python311\Lib\copy.py:172, in deepcopy(x, memo, _nil)
170 y = x
171 else:
--> 172 y = _reconstruct(x, memo, *rv)
174 # If is its own copy, don't memoize.
175 if y is not x:

File ~\AppData\Local\Programs\Python\Python311\Lib\copy.py:271, in _reconstruct(x, memo, func, args, state, listiter, dictiter, deepcopy)
269 if state is not None:
270 if deep:
--> 271 state = deepcopy(state, memo)
272 if hasattr(y, '__setstate__'):
273 y.__setstate__(state)

File ~\AppData\Local\Programs\Python\Python311\Lib\copy.py:146, in deepcopy(x, memo, _nil)
144 copier = _deepcopy_dispatch.get(cls)
145 if copier is not None:
--> 146 y = copier(x, memo)
147 else:
148 if issubclass(cls, type):

File ~\AppData\Local\Programs\Python\Python311\Lib\copy.py:231, in _deepcopy_dict(x, memo, deepcopy)
229 memo[id(x)] = y
230 for key, value in x.items():
--> 231 y[deepcopy(key, memo)] = deepcopy(value, memo)
232 return y

File ~\AppData\Local\Programs\Python\Python311\Lib\copy.py:172, in deepcopy(x, memo, _nil)
170 y = x
171 else:
--> 172 y = _reconstruct(x, memo, *rv)
174 # If is its own copy, don't memoize.
175 if y is not x:

File ~\AppData\Local\Programs\Python\Python311\Lib\copy.py:265, in _reconstruct(x, memo, func, args, state, listiter, dictiter, deepcopy)
263 if deep and args:
264 args = (deepcopy(arg, memo) for arg in args)
--> 265 y = func(*args)
266 if deep:
267 memo[id(x)] = y

Cell In[75], line 178, in Space.__hash__(self)
177 def __hash__(self):
--> 178 return hash((self._id, self._name, self._building, self._section))

AttributeError: 'Space' object has no attribute '_id'


Подробнее здесь: https://stackoverflow.com/questions/783 ... -attribute
Реклама
Ответить Пред. темаСлед. тема

Быстрый ответ

Изменение регистра текста: 
Смайлики
:) :( :oops: :roll: :wink: :muza: :clever: :sorry: :angel: :read: *x)
Ещё смайлики…
   
К этому ответу прикреплено по крайней мере одно вложение.

Если вы не хотите добавлять вложения, оставьте поля пустыми.

Максимально разрешённый размер вложения: 15 МБ.

  • Похожие темы
    Ответы
    Просмотры
    Последнее сообщение

Вернуться в «Python»