在Python中:“多对多”的容器是什么?关系?

时间:2017-10-25 18:22:47

标签: python containers

我开始编写一个代码,可以在游戏板上循环播放maaaaany以解决一些不受关注的问题。

我必须检查其位置取决于其他单元格(B' s)属性的单元格(A' s)。我考虑将它存储在一个集合中,而不是重新猜测这种依赖性。此操作将在两个方向上完成(也可以访问A')。

换句话说,我需要一个能够存储多个2个关系的集合,我关心的是检索对象的性能。我也关心插入和删除的时间,因为我的电路板会随着时间的推移而改变,所以我需要插入新的关系并删除其他关系。

我不确切知道插入/删除与访问之间的比例是什么..

可以使用什么容器? 我不知道内置或任何lib 这样的容器,所以我尝试了以下内容。我希望你能给我一些建议,使用一些天才的lib,或者比我使用的更好的模式。

# -*- coding: utf-8 -*-


from timeit import timeit
from collections import defaultdict
import random

N = 1000
r = range(1,N+1)
numbers = list(r)
divs = list(r)


def build(container_cls):
    container = container_cls()

    for item in build_set():
        container.add(*item)

    return container

def build_set():
    output = set()

    for n in numbers:
        for d in divs:
            if n % d == 0:
                output.add((n,d))

    return output

class Container_1:
    def __init__(self):
        self._a2b = defaultdict(set)

    def add(self, a, b):
        self._a2b[a].add(b)

    def remove(self, a, b):
        self._a2b[a].remove(b)

    def bs_from_a(self, a):
        return self._a2b[a]

    def as_from_b(self, b):
        return set(a for a,bs in self._a2b.items() if b in bs )

    def items(self):
        return set((a,b) for a,bs in self._a2b.items() for b in bs)

    def __contains__(self, item) :
        try :
            return item[1] in self._a2b[item[0]]
        except IndexError:
            return False


class Container_2:
    def __init__(self):
        self._a2b = defaultdict(set)
        self._b2a = defaultdict(set)

    def remove(self, a, b):
        self._a2b[a].remove(b)
        self._b2a[b].remove(a)

    def add(self, a, b):
        self._a2b[a].add(b)
        self._b2a[b].add(a)

    def bs_from_a(self, a):
        return self._a2b[a]

    def as_from_b(self, b):
        return self._b2a[b]

    def items(self):
        return set((a,b) for a,bs in self._a2b.items() for b in bs)

    def __contains__(self, item) :
        try :
            return item[1] in self._a2b[item[0]]
        except IndexError:
            return False

class Container_3:
    def __init__(self):
        self._items = set()

    def remove(self, a, b):
        self._items.remove((a,b))

    def add(self, a, b):
        self._items.add((a,b))

    def bs_from_a(self, a):
        return set(bb for aa, bb in self._items if aa == a)

    def as_from_b(self, b):
        return set(aa for aa, bb in self._items if bb == b)

    def items(self):
        return set(self._items)

    def __contains__(self, item) :
        return item in self._items


def time_wrap(command, setup):
    N = 10
    result = timeit(command, setup, number=N)

    print("Setup    :",  setup)
    print("Command  :", command)
    print("Result   :", result)

def time_container(container_cls):
    args = {
            "cls" : container_cls.__name__,
            "N"   : N
    }

    print("\n\n===== {cls} =====".format(**args))
    print("---Bulk Add---")
    setup = "from __main__ import {cls}, build_set; c={cls}(); items=build_set()".format(**args)
    command = "for item in items: c.add(*item)".format(**args)
    time_wrap(command,setup)

    print("---As from B---")
    setup = "from __main__ import {cls}, build; c = build({cls})".format(**args)
    command = "for i in range({N}): c.as_from_b(i)".format(**args)
    time_wrap(command,setup)

    print("---Bs from A---")
    setup = "from __main__ import {cls}, build; c = build({cls})".format(**args)
    command = "for i in range({N}): c.bs_from_a(i)".format(**args)
    time_wrap(command,setup)

    print("---Get all items")
    setup = "from __main__ import {cls}, build; c = build({cls})".format(**args)
    command = "c.items()".format(**args)
    time_wrap(command,setup)

    print("---Remove all items")
    setup = "from __main__ import {cls}, build; c = build({cls})".format(**args)
    command = "for item in c.items(): c.remove(*item)".format(**args)
    time_wrap(command,setup)

    print("---Find if in")
    setup = "from __main__ import {cls}, build; from itertools import product; c = build({cls})".format(**args)
    command = "for a,b in product(*[range(4)]*2): (a,b) in c".format(**args)
    time_wrap(command,setup)

def assert_containers_match(container_cls_1,container_cls_2):
    ctn_1 = build(container_cls_1)
    ctn_2 = build(container_cls_2)

    def assert_match():
        assert ctn_1.items() == ctn_2.items()

        for i in r:
            assert ctn_1.bs_from_a(i) == ctn_2.bs_from_a(i)
            assert ctn_1.as_from_b(i) == ctn_2.as_from_b(i)

    assert_match()

    # Tests for removal
    # Only tests 10 samples because it's is too time consuming
    for item in random.sample( ctn_1.items(), 10) :
        assert item in ctn_1
        assert item in ctn_2

        ctn_1.remove(*item)
        ctn_2.remove(*item)

        assert item not in ctn_1
        assert item not in ctn_2

        assert_match()



assert_containers_match(Container_1, Container_2)
assert_containers_match(Container_1, Container_3)
time_container(Container_1)
time_container(Container_2)
time_container(Container_3)

以下是输出计算时间:

===== Container_1 =====
---Bulk Add---
Setup    : from __main__ import Container_1, build_set; c=Container_1(); items=build_set()
Command  : for item in items: c.add(*item)
Result   : 0.015180546937699546
---As from B---
Setup    : from __main__ import Container_1, build; c = build(Container_1)
Command  : for i in range(1000): c.as_from_b(i)
Result   : 0.3854760317099135
---Bs from A---
Setup    : from __main__ import Container_1, build; c = build(Container_1)
Command  : for i in range(1000): c.bs_from_a(i)
Result   : 0.0014935762328605051
---Get all items
Setup    : from __main__ import Container_1, build; c = build(Container_1)
Command  : c.items()
Result   : 0.008290981613299664
---Remove all items
Setup    : from __main__ import Container_1, build; c = build(Container_1)
Command  : for item in c.items(): c.remove(*item)
Result   : 0.003233156124224479
---Find if in
Setup    : from __main__ import Container_1, build; from itertools import product; c = build(Container_1)
Command  : for a,b in product(*[range(4)]*2): (a,b) in c
Result   : 4.729901911559864e-05


===== Container_2 =====
---Bulk Add---
Setup    : from __main__ import Container_2, build_set; c=Container_2(); items=build_set()
Command  : for item in items: c.add(*item)
Result   : 0.023733136010378075
---As from B---
Setup    : from __main__ import Container_2, build; c = build(Container_2)
Command  : for i in range(1000): c.as_from_b(i)
Result   : 0.0014887000452290522
---Bs from A---
Setup    : from __main__ import Container_2, build; c = build(Container_2)
Command  : for i in range(1000): c.bs_from_a(i)
Result   : 0.0014411572165045072
---Get all items
Setup    : from __main__ import Container_2, build; c = build(Container_2)
Command  : c.items()
Result   : 0.008764215610426618
---Remove all items
Setup    : from __main__ import Container_2, build; c = build(Container_2)
Command  : for item in c.items(): c.remove(*item)
Result   : 0.004034069921999617
---Find if in
Setup    : from __main__ import Container_2, build; from itertools import product; c = build(Container_2)
Command  : for a,b in product(*[range(4)]*2): (a,b) in c
Result   : 4.705520950665232e-05


===== Container_3 =====
---Bulk Add---
Setup    : from __main__ import Container_3, build_set; c=Container_3(); items=build_set()
Command  : for item in items: c.add(*item)
Result   : 0.016531982304513804
---As from B---
Setup    : from __main__ import Container_3, build; c = build(Container_3)
Command  : for i in range(1000): c.as_from_b(i)
Result   : 2.7582481321151135
---Bs from A---
Setup    : from __main__ import Container_3, build; c = build(Container_3)
Command  : for i in range(1000): c.bs_from_a(i)
Result   : 2.835941646664196
---Get all items
Setup    : from __main__ import Container_3, build; c = build(Container_3)
Command  : c.items()
Result   : 0.0014294543666437676
---Remove all items
Setup    : from __main__ import Container_3, build; c = build(Container_3)
Command  : for item in c.items(): c.remove(*item)
Result   : 0.0019024445550712699
---Find if in
Setup    : from __main__ import Container_3, build; from itertools import product; c = build(Container_3)
Command  : for a,b in product(*[range(4)]*2): (a,b) in c
Result   : 4.291044979254366e-05

0 个答案:

没有答案