我有几个类,并希望使用像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或自定义包中有类似的东西吗?
如果没有,我必须自己编写,继承的最佳选择是什么?
答案 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()