将数据库驱动型Python脚本转换为非数据库驱动型面向对象脚本

70 阅读4分钟

您有一款严重依赖MySQL且没有任何类定义的Python软件。出于性能原因,以及数据库实际上只是用于存储和检索大量数据的原因,您希望将其转换为根本不使用数据库的面向对象的Python脚本。

  1. 解决方案

您的计划是将数据库表导出到一组文件(数量不多,因为这是一个非常简单的数据库;它很大,因为它有很多行,但只有几张表,每张表只有两到三列)。

然后,您计划读取数据,并提供一组函数来访问和操作数据。

方法一:使用sqlite

如果您不希望添加任何额外的框架,因为您希望该代码易于传输到不同的计算机,那么可以将数据转换为sqlite。它具有可移植性,只需一个文件即可移动数据库,并且在任何安装了Python(至少为2.5)的地方都可以使用sqlite。

方法二:创建类和对象

一般来说,您希望对象与“现实世界实体”完全匹配。 由于您从数据库开始,因此数据库不一定具有任何现实世界保真度。一些数据库设计简直太糟糕了。 如果您的数据库具有合理的水果模型,那么这就是您的起点。首先弄清楚。 “集合”可能是算法解决方案的一部分的人工构造,而不是问题本身的真正部分。通常,集合是问题的一部分,您还应该设计这些类。 然而,有时集合是使用数据库的人工制品,一个简单的Python列表就是您所需要的。 在其他情况下,集合实际上是从某些唯一键值到某个实体的正确映射,在这种情况下,它是Python字典。 有时,集合是从某个非唯一键值到一些实体集合的正确映射,在这种情况下,它是Python collections.defaultdict(list)。 从基本实体开始,这些实体类似于现实世界。这些实体获取类定义。 集合可以使用内置Python集合或可能需要它们自己的类。

方法三:使用namedtuple

在简单的情况下,namedtuple可以让您开始:

  1. from collections import namedtuple
  2. Fruit = namedtuple("Fruit", "name weight color")
  3. fruits = [Fruit(*row) for row in cursor.execute('select * from fruits')]

Fruit等效于以下类:

  1. Fruit = namedtuple("Fruit", "name weight color", verbose=True) class Fruit(tuple): 'Fruit(name, weight, color)'

     __slots__ = ()
    
     _fields = ('name', 'weight', 'color')
    
     def __new__(cls, name, weight, color):
         return tuple.__new__(cls, (name, weight, color))
    
     @classmethod
     def _make(cls, iterable, new=tuple.__new__, len=len):
         'Make a new Fruit object from a sequence or iterable'
         result = new(cls, iterable)
         if len(result) != 3:
             raise TypeError('Expected 3 arguments, got %d' % len(result))
         return result
    
     def __repr__(self):
         return 'Fruit(name=%r, weight=%r, color=%r)' % self
    
     def _asdict(t):
         'Return a new dict which maps field names to their values'
         return {'name': t[0], 'weight': t[1], 'color': t[2]}
    
     def _replace(self, **kwds):
         'Return a new Fruit object replacing specified fields with new values'
         result = self._make(map(kwds.pop, ('name', 'weight', 'color'), self))
         if kwds:
             raise ValueError('Got unexpected field names: %r' % kwds.keys())
    
         return result
    
     def __getnewargs__(self):
         return tuple(self)
    
     name = property(itemgetter(0))
     weight = property(itemgetter(1))
     color = property(itemgetter(2))
    

方法四:使用ZODB

另一种方法是使用ZODB直接持久地存储对象。你唯一要做的就是让你的类继承自Peristent,这样从根对象开始的一切东西就会自动以对象的形式存储在该数据库中。根对象来自ZODB连接。有许多后端可用,默认值只是一个文件。

然后,一个类可以看起来像这样:

class Collection(persistent.Persistent):

  def __init__(self, fruit = []):
      self.fruit = fruit

class Fruit(peristent.Persistent):

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

假设您有根对象,那么您可以这样做:

fruit = Fruit("apple")
root.collection = Collection([fruit])

它会自动存储在数据库中。您可以通过简单地从根对象访问“collection”来再次找到它:

print root.collection.fruit

您还可以像往常一样从例如Fruit派生子类。 有关更多信息的有用链接:

新的ZODB主页 ZODB教程

这样,您仍然可以使用Python对象的所有功能并且不需要通过ORM对某些内容进行序列化,但是您仍然可以轻松存储数据。

方法五:抽象对象类的持久性

将所有持久性逻辑放入适配器类中,并将适配器分配给对象类。就像:

class Fruit(Object):

   @classmethod
   def get(cls, id):
      return cls.adapter.get(id)

   def put(self):
      cls.adapter.put(self)

   def __init__(self, id, name, weight, color):
      self.id = id
      self.name = name
      self.weight = weight
      self.color = color


 class FruitAdapter(Object):

    def get(id):
       # retrieve attributes from persistent storage here
       return Fruit(id, name, weight, color)

    def put(fruit):
       # insert/update fruit in persistent storage here

 Fruit.adapter = FruitAdapter()
 f = Fruit.get(1)
 f.name = "lemon"
 f.put()
 # and so on...

现在,您可以构建与您确定的任何持久性格式(数据库、平面文件、内存集合等)进行交互的不同FruitAdapter对象,基本Fruit类将完全不受影响。