Python中是否有类型化的收集工具?

时间:2012-03-21 08:22:05

标签: python collections

我有几个类,并希望使用像DB(或Django ORM,但更简单)的集合。使用数据库将是一个巨大的开销,所以我更喜欢在内存中具有类似的功能:

>>> node = Nodes.create(lat=X,lon=Y)
>>> node.id
1

>>> Nodes.all()
[<Node 1>]

>>> Nodes[1]  # by id
[<Node 1>]

>>> way_nodes = map(Nodes.create, ((X, Y), (Z, W)))
>>> way = Ways.create(way_nodes)
>>> way.nodes
[<Node 2>, <Node 3>]

>>> way.id
1

这基本上就是我所需要的。 Python或自定义包中有类似的东西吗?

如果没有,我必须自己编写,继承的最佳选择是什么?

3 个答案:

答案 0 :(得分:2)

使用适当的关系数据库不一定是一个巨大的开销。你知道SQLite吗? (python module sqlite3)它可以与内存数据库一起使用。

import sqlite3
db_conn = sqlite3.connect(":memory:")
db_conn.execute("CREATE TABLE nodes (id INTEGER PRIMARY KEY AUTOINCREMENT, lat FLOAT, long FLOAT);")

但是对于你想要做的事情,我不确定你是否需要任何关系语义。 (您似乎正在做的是创建Nodes的可索引列表 - node_id : Node objects的词典会为您做什么?


顺便说一句,map()确实存在于Python中,但是使用列表理解它被认为更“Pythonic”。使用列表推导的原因是:

  • 语法更直观
  • 列表推导结合了map()filter()的功能。

而不是说

way_nodes = map(Nodes.create, ((X,Y),(Z,W))

我们会说

way_nodes = [Nodes.create(*coord_pair) for coord_pair in ((X,Y),(Z,W))]

两者都同样正确,但第二种形式是首选。

答案 1 :(得分:2)

这些似乎是非常基本的要求,为什么不将它们编入你的课程?

from itertools import count, starmap

class Node(object):
    nextid = count(1).next
    nodes = {}
    def __init__(self, lat, lon):
        self.lat, self.lon = lat, lon
        self.id = self.nextid()
        self.nodes[self.id] = self

    @classmethod
    def all(cls):
        return cls.nodes.values()

    def __repr__(self):
        return '<Node %s>' % self.id

    @classmethod
    def byid(cls, id):
        return cls.nodes[id]

class Way(object):
    nextid = count(1).next
    def __init__(self, nodes):
        self.nodes = nodes[:]
        self.id = self.nextid()

node = Node(lat='X',lon='Y')
print node.id
print Node.all()
print Node.byid(1)

way_nodes = list(starmap(Node, (('X', 'Y'), ('Z', 'W'))))
way = Way(way_nodes)
print way.nodes
print way.id

答案 2 :(得分:0)

如果node是一个列表,您可以使用内置列表类型:

node[0] # get by id (id == 0)
node # .all()

如果您想创建特殊类来实现所需的接口,那么通过编写与您的自定义类相同数量的代码,您可以使用SQLAlchemy获得关系数据库的全部功能:

# get node id
node.id # -> 1
# get all nodes
Node.query.all() # -> [<Node 1>, <Node 2>, <Node 3>, <Node 4>]
# get all nodes associated with the `way`
way.nodes # -> [<Node 2>, <Node 3>, <Node 4>]
# get `way` with id == 1
Way.query.get(1) # -> <Way 1>
# get all `way`s the node belongs to
node.ways.all() # -> []
# get all nodes with longitude == Y
Node.query.filter_by(lon=Y).all() # -> [<Node 1>, <Node 2>]

以下是实现它的代码:

from sqlalchemy import create_engine, Column, Integer, Float, Table, ForeignKey
from sqlalchemy.orm import scoped_session, sessionmaker, relationship, backref
from sqlalchemy.ext.declarative import declarative_base, declared_attr

# declare models
class Base(declarative_base()):
    __abstract__ = True

    # provide id for each object
    id = Column(Integer, primary_key=True)

    @classmethod
    def create(cls, *args, **kwargs):
        obj = cls(*args, **kwargs)
        Session.add(obj) # add to db session
        return obj

    @declared_attr
    def __tablename__(cls):
        """Use lowercased classname for table name."""
        return cls.__name__.lower()

    def __repr__(self):
        return "<{name} {id}>".format(name=self.__class__.__name__, id=self.id)

class Node(Base):
    lat = Column(Float)
    lon = Column(Float)

    def __init__(self, lat, lon):
        self.lat = lat
        self.lon = lon

# define many-to-many relationship
nodes = Table(
    'nodes', Base.metadata,
    Column('node_id', Integer, ForeignKey('node.id')),
    Column('way_id', Integer, ForeignKey('way.id'))
    )

class Way(Base):
    nodes = relationship(Node, secondary=nodes,
                         backref=backref('ways', lazy='dynamic'))

    def __init__(self, nodes):
        self.nodes = nodes

测试

# create in memory db
engine = create_engine('sqlite://')
# one db session per thread
Session = scoped_session(sessionmaker(bind=engine))
Base.query = Session.query_property() # allow queries via Model.query
#
Base.metadata.create_all(bind=engine)


X, Y, Z, W = range(4)
node = Node.create(lat=X, lon=Y)
way_nodes = [Node.create(*args) for args in [(X, Y), (Z, W)]]
way = Way.create(way_nodes)
way.nodes.append(Node(10, 11))

Session.commit()
assert node.id == 1
assert node.ways.all() == []
assert Node.query.filter_by(lon=Y).count() == 2
assert way.id == 1
assert any(x.lat == 10 and x.lon == 11 for x in way.nodes)
assert Node.query.filter_by(lat=10, lon=11).one()
assert Way.query.get(1) == way

# remove context (thread) -local session
Session.remove()