代码之家  ›  专栏  ›  技术社区  ›  Ben

将数据库驱动的(非oo)python脚本转换为非数据库驱动的oo脚本

  •  0
  • Ben  · 技术社区  · 17 年前

    我有一些严重依赖于MySQL的软件,它是用没有任何类定义的Python编写的。出于性能方面的考虑,由于数据库实际上只是用来存储和检索大量数据,所以我想将其转换为一个完全不使用数据库的面向对象的Python脚本。

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

    然后,我计划在中读取数据,并拥有一组提供对数据的访问和操作的函数。

    我的问题是:

    是否有将一组数据库表转换为类和对象的首选方法?例如,如果我有一个包含水果的表,其中每个水果都有一个ID和一个名称,我会有一个包含“水果”对象列表的“collectionoffruit”类,还是只需要一个包含元组列表的“collectionoffruit”类?或者我要一份水果的清单?

    我不想添加任何额外的框架,因为我希望这个代码易于传输到不同的机器。所以我只是在寻找关于如何表示可能更自然地存储在数据库表、Python对象中的数据的一般性建议。

    或者,我是否应该读一本好书,这本书会为我指明正确的方向?

    8 回复  |  直到 14 年前
        1
  •  5
  •   jlc    17 年前

    如果数据自然适合数据库表(“矩形数据”),为什么不将其转换为sqlite?它是可移植的——只需一个文件就可以移动数据库,而sqlite在任何有python的地方都可以使用(无论如何都是2.5或更高版本)。

        2
  •  2
  •   S.Lott    17 年前

    一般来说,您希望对象与您的“真实世界实体”完全匹配。

    因为您是从数据库开始的,所以数据库并不总是具有任何真实的保真度。有些数据库设计很糟糕。

    如果你的数据库有合理的水果模型,那就是你开始的地方。先把它弄好。

    一个“集合”可能——也可能不是——是一个人工构造,它是求解算法的一部分,实际上不是问题的一部分。通常是集合 问题的一部分,您还应该设计这些类。

    然而,在其他时候,这个集合是一个使用数据库的人工制品,您只需要一个简单的python列表。

    还有一些时候,集合实际上是从某个惟一的键值到实体的适当映射,在这种情况下,它是一个Python字典。

    有时,集合是从一些非唯一的键值到一些实体集合的适当映射,在这种情况下,它是一个python collections.defaultdict(list) .

    从基本的、真实的实体开始。它们得到类定义。

    集合可以使用内置的Python集合,或者可能需要它们自己的类。

        3
  •  1
  •   dwc    17 年前

    没有“一刀切”的答案——这将很大程度上取决于数据以及它在应用程序中的使用方式。如果数据和用法足够简单,您可能希望将水果存储在以id为键的dict中,其余数据存储为元组。或者没有。这完全取决于。如果有一个指导原则,那就是提取应用程序的基本需求,然后根据这些需求编写代码。

        4
  •  1
  •   Sujoy    17 年前

    您可以有一个包含id和name实例变量的水果类。以及一个从文件中读取/写入信息的函数,可能还有一个类变量来跟踪创建的水果(对象)的数量。

        5
  •  1
  •   jfs    17 年前

    在简单的情况下 namedtuples 让我们开始吧:

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

    Fruit 相当于以下类:

    >>> 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))
    
        6
  •  1
  •   MrTopf    17 年前

    另一种方法是使用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
    

    您也可以像往常一样从例如水果派生子类。

    包含更多信息的有用链接:

    这样,您仍然能够使用Python对象的全部功能,并且无需序列化某些内容(例如,通过ORM),但仍然有一种存储数据的简单方法。

        7
  •  1
  •   Sam Corder    17 年前

    这里有几个要点供您考虑。如果您的数据很大,那么将其全部读取到内存中可能是浪费。如果您需要随机访问,而不仅仅是顺序访问数据,那么您要么每次扫描最多整个文件,要么将该表读取到索引内存结构(如字典)中。列表仍然需要某种扫描(直接迭代或二进制搜索,如果排序)。也就是说,如果您不需要DB的某些功能,那么就不要使用它,但是如果您只是认为MySQL太重,那么就使用前面的sqlite建议中的+1。它为您提供了使用数据库时所需的大部分功能,而不需要并发开销。

        8
  •  1
  •   Robert Rossney    17 年前

    从对象类中抽象持久性。将所有持久性逻辑放在适配器类中,并将适配器分配给对象类。类似:

    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...
    

    现在,您可以构建不同的水果适配器对象,这些对象可以与您所确定的任何持久性格式(数据库、平面文件、内存中的集合,等等)进行互操作,并且基本的水果类将完全不受影响。