понедельник, 30 мая 2011 г.

Работаем с ссылками на словари в python

Не всегда то, что в начале кажется недостатком, таковым является и в действительности. В одном из предыдущих постов "Тождественность в python на примере списков" указывалось на опасность при передаче одной переменной значения другой. Особенно, если она является списком или словарем.

Рассмотрим на примере словарей, как наиболее сложной структуры, чем те же списки.

Создадим новый словарь
>>> dict1 = dict()
>>> id(dict1)
17059072
Добавим несколько пар ключ/значение
>>> dict1['key1'] = 'value1'
>>> dict1['key2'] = 'value2'
>>> dict1
{'key2': 'value2', 'key1': 'value1'}
Присвоим новой переменной dict2 значение dict1
>>> dict2 = dict1
>>> id(dict1),id(dict2)
(17059072, 17059072)
Видно, что при данной операции произошло не копирование содержимого dict1 в dict2, а dict2 стал ссылаться на тот же объект, что и dict1. Если добавить для dict2 новую пару ключ/значение
>>> dict2['key3'] = 'value3'
>>> dict2
{'key3': 'value3', 'key2': 'value2', 'key1': 'value1'}
>>> dict1
{'key3': 'value3', 'key2': 'value2', 'key1': 'value1'}
>>> id(dict1),id(dict2)
(17059072, 17059072)
то видно, что добавление новой пары ключ/значение для dict2 приводит к изменению и dict1. Изменяется содержимое объекта, ссылки на него остаются неизменными. Если необходимо сделать копию словаря, что для этого необходимо воспользоваться операцией deep copy, которая создает новый объект и рекурсивно копирует все объекты в него. В python нет встроенной функции для deep copy объектов, но есть функция из стандартной библиотеки copy.deepcopy(), которую можно использовать для этих целей
>>> import copy
>>> dict3 = copy.deepcopy(dict1)
>>> dict3
{'key3': 'value3', 'key2': 'value2', 'key1': 'value1'}
>>> id(dict1),id(dict3)
(17059072, 17070976) 
На примере простой симуляции “Сбор урожая” рассмотрим, каким образом можно использовать свойства python оперирования ссылками на объекты и соответственно их содержанием.

У нас есть комбайн:
class Harvester:
    def __init__(self):
        self.id = id(self)
        self.storage = None
   
    def run(self):
        self.storage.append(int(random.random() * 100)) 
У каждого комбайна есть его уникальный идентификатор id, позволяющий в дальнейшем определить сколько этот комбайн собрал урожая. Есть определенное место storage на складе, где складывается урожай. Каждый комбайн в зависимости от неких условий может собрать за раз разное количество урожая.

У нас также есть склад:
class Warehouse:
    def __init__(self):
        self.harvesters = []
        self.slots = {}

        for i in xrange(5):
            h = Harvester()
            self.slots[h.id] = []
            h.storage = self.slots[h.id]
            self.harvesters.append(h)
           
    def run(self):
        for step in xrange(10):
            for u in self.harvesters:
                u.run()
Склад контролирует работу комбайнов, выделяет место для хранения урожая. В данном примере со складом работают 5 комбайнов. Код, ради которого и создавался данный пример выглядит следующим образом:
self.slots[h.id] = []
h.storage = self.slots[h.id]
Во второй строке мы передаем ссылку на “место хранения урожая на складе”. Таким образом комбайн может сохранять данные в строго определенном для него месте. Комбайн оперирует отведенным для него местом хранения, ничего не зная про хранилища других комбайнов.

Запустим склад в работу
w = Warehouse()
# склад до начала уборки урожая
print w.slots
w.run()
# склада после уборки
print w.slots
Результат выполнения будет выглядеть следующим образом

{10996552: [], 10997704: [], 11042472: [], 11042400: [], 11042328: []}

{10996552: [91, 78, 60, 39, 39, 50, 24, 5, 18, 75], 10997704: [56, 38, 57, 47, 44, 40, 82, 43, 76, 65], 11042472: [25, 94, 48, 72, 32, 92, 21, 45, 94, 36], 11042400: [73, 67, 28, 70, 60, 57, 11, 52, 93, 58], 11042328: [82, 37, 77, 36, 21, 89, 66, 2, 73, 71]}


Пока все просто :)

0 комментариев:

Отправить комментарий