Флаги в Питоне - PullRequest
       37

Флаги в Питоне

4 голосов
/ 29 июня 2009

Я работаю с большой матрицей (250x250x30 = 1 875 000 ячеек), и мне нужен способ установить произвольное количество флагов для каждой ячейки в этой матрице, в некотором роде, который прост в использовании и достаточно компактен .

Мой первоначальный план представлял собой массив списков размером 250x250x30, где каждый элемент был чем-то вроде: ["FLAG1","FLAG8","FLAG12"]. Затем я изменил его на хранение только целых чисел: [1,8,12]. Эти целые числа внутренне отображаются функциями получения / установки в исходные строки флагов. При этом используется только 250 МБ с 8 флагами на точку, что хорошо с точки зрения памяти.

У меня вопрос: я упускаю другой очевидный способ структурировать данные такого рода?

Спасибо всем за ваши предложения. Я закончил тем, что свел несколько предложений в одно, к сожалению, я могу выбрать только один ответ и вынужден согласиться с другими:

РЕДАКТИРОВАТЬ: эээ исходный код, который я имел здесь (используя наборы в качестве базового элемента трехмерного массива), использовал много памяти. Эта новая версия использует около 500 МБ при заполнении randint(0,2**1000).

import numpy

FLAG1=2**0
FLAG2=2**1
FLAG3=2**2
FLAG4=2**3

(x,y,z) = (250,250,30)

array = numpy.zeros((x,y,z), dtype=object)


def setFlag(location,flag):
    array[location] |= flag
def unsetFlag(location,flag):
    array[location] &= ~flag

Ответы [ 6 ]

7 голосов
/ 29 июня 2009

Ваше решение хорошо, если каждая клетка будет иметь флаг. Однако, если вы работаете с разреженным набором данных, где только небольшая часть ваших ячеек будет иметь флаги, то вам действительно нужен словарь. Вы хотели бы установить диктонар, чтобы ключ являлся кортежем для расположения ячейки, а значение представляло собой список флагов, которые есть в вашем решении.

allFlags = {(1,1,1):[1,2,3], (250,250,30):[4,5,6]}

Здесь у ячейки 1,1,1 есть флаги 1,2, а у 3, а у ячейки 250,250,30 есть флаги 4,5 и 6

edit- исправлены кортежи, спасибо Андре, и синтаксис словаря.

5 голосов
/ 29 июня 2009

Вы можете определить некоторые константы с разными степенями двух значений как:

FLAG1 = 0x01
FLAG8 = 0x02
FLAG12 = 0x04
...

И использовать их с логической логикой, чтобы хранить флаги только в одном целом числе, p.e.:

flags = FLAG1 | FLAG8

Чтобы проверить, включен ли флаг, вы можете использовать оператор &:

flag1_enabled = flags & FLAG1

Если флаг включен, это выражение будет возвращать ненулевое значение, которое будет оцениваться как True в любой логической операции. Если флаг отключен, выражение вернет 0, что оценивается как False в логических операциях.

4 голосов
/ 29 июня 2009

Я бы обычно использовал массив numpy (предположительно, из коротких целых чисел, по 2 байта каждый, поскольку вам может потребоваться более 256 различных значений) - это заняло бы менее 4 МБ для <2 миллионов ячеек . </p>

Если бы по какой-то причине я не мог позволить себе зависимость numpy (например, на App Engine, которая не поддерживает numpy), я бы использовал модуль стандартной библиотеки array - он поддерживает только 1- размерные массивы, но он не менее эффективен по объему, чем numpy для больших однородных массивов, а упомянутые вами процедуры получения / установки вполне могут «линеаризовать» кортеж из 3 элементов, который является вашим естественным, в единый целочисленный индекс в 1-D массив.

В общем, рассматривайте numpy (или массив) всякий раз, когда у вас есть большие однородные, плотные векторы или матрицы чисел - встроенные списки Python очень бесполезны в этом случае (из-за их общности, которую вы не используется и не нужен здесь! -), а экономия памяти косвенно приводит к экономии времени (лучшее кэширование, меньше уровней косвенности и т. д. и т. д.).

3 голосов
/ 29 июня 2009

Рассмотрите возможность использования шаблона Flyweight для совместного использования свойств ячейки:

http://en.wikipedia.org/wiki/Flyweight_pattern

1 голос
/ 29 июня 2009

На шаг впереди предложение Робби ...

flags = set()
x, y, flag = 34, 201, 3
flags.add((x, y, flag)) # set flag 3 at position (34, 201)
if (3, 2, 1) in flags: # check if flag 1 is at position (3, 2)
    # do something
else:
    # do something else

Вы также можете создать вспомогательный класс.

class Flags(object):
    def __init__(self):
        self.data = set()
    def add(self, x, y, flag):
        self.data.add((x, y, flag))
    def remove(self, x, y, flag):
        self.data.remove((x, y, flag))
    def contains(self, x, y, flag):
        return (x, y, flag) in self.data

Вы также можете реализовать специальные методы Python, такие как __contains__, чтобы упростить работу.

1 голос
/ 29 июня 2009

BitSet - это то, что вам нужно, поскольку он позволяет хранить много флагов одновременно, используя только целое число фиксированного размера (тип Int)

...