用于python的dao:用于创建简单且一致的接口,以访问复杂而多样的数据源的工具。

py2store的Python项目详细描述


py2存储

存储积垢的方式和位置。

列出、读取、写入和删除结构化数据源/目标中的数据, 好像操作简单的python内置代码(dict、list)或通过您想与之交互的接口一样, 不受外形或物理特性的影响。 此外,可以在不更改业务逻辑代码的情况下更改这些特殊性。

快速启动

安装它(例如pip install py2store)。

想一想你想使用的存储类型,然后像使用dict一样继续。 下面是本地存储的一个示例(您必须仅在此处字符串键)。

>>> from py2store import QuickStore
>>>
>>> store = QuickStore()  # will print what (tmp) rootdir it is choosing
>>> # Write something and then read it out again
>>> store['foo'] = 'baz'
>>> store['foo']
'baz'
>>> 
>>> # Go see that there is now a file in the rootdir, named 'foo'!
>>> 
>>> # Write something more complicated
>>> store['hello/world'] = [1, 'flew', {'over': 'a', "cuckoo's": map}]
>>> store['hello/world'] == [1, 'flew', {'over': 'a', "cuckoo's": map}]
True
>>>
>>> # do you have the key 'foo' in your store?
>>> 'foo' in store
True
>>> # how many items do you have now?
>>> assert len(store) >= 2  # can't be sure there were no elements before, so can't assert == 2
>>> 
>>> # delete the stuff you've written
>>> del store['foo']
>>> del store['hello/world']

quickstore默认情况下将使用pickle作为序列化程序在本地文件中存储内容。 如果未指定根目录, 它将使用它将创建的tmp目录(第一次尝试存储某些内容时) 它将创建任何需要创建以满足任何/key/that/contains/slashes的目录。 当然,一切都是可配置的。

更多示例

看起来像一个dict

下面,我们将创建一个默认存储并演示一些基本操作。 默认存储使用dict作为后端持久器。 dict既不是真正的后端,也不是持久化的。但是尝试一下有助于 足迹。

frompy2store.baseimportStores=Store()assertlist(s)==[]s['foo']='bar'# put 'bar' in 'foo'assert'foo'ins# check that 'foo' is in (i.e. a key of) sasserts['foo']=='bar'# see that the value that 'foo' contains is 'bar'assertlist(s)==['foo']# list all the keys (there's only one)assertlist(s.items())==[('foo','bar')]# list all the (key, value) pairsassertlist(s.values())==['bar']# list all the valuesassertlen(s)==1# Number of items in my stores['another']='item'# store another itemassertlen(s)==2# Now I have two!assertlist(s)==['foo','another']# here they are

上面的代码没有什么特别之处。 我刚刚演示了一个录音机的一些操作。 但py2store的目标正是这种简单性。 现在您可以将s=store()替换为s=anotherstore(…)其中anotherstore 现在使用一些其他后端,这些后端可以是远程或本地的,也可以是数据库,或者任何 可以将某物(值)存储在某处的系统。(键)。

您可以从现有存储中选择(例如本地文件、AWS S3、MongoDB)或 很容易自己制作(稍后将详细介绍)。

但是,看起来你还是在和一个听写员说话。这不仅意味着你可以 与各种存储系统交谈,而不必实际学习如何,但也意味着 您编写的相同业务逻辑代码可以在不做任何修改的情况下重用。

但是py2store提供的不仅仅是一个简单的一致的facade,它可以存储东西, 但也提供了定义如何做到这一点的方法。

在键值存储的情况下,"how"是基于键值定义的(如何引用) 存储的对象和值(如何序列化和反序列化这些对象)。

转换关键点:相对路径和绝对路径

请看下面的示例,它将向存储区添加一层密钥转换。

# defining the storefrompy2store.baseimportStoreclassPrefixedKeyStore(Store):prefix=''def_id_of_key(self,key):returnself.prefix+key# prepend prefix before passing on to storedef_key_of_id(self,_id):ifnot_id.startswith(self.prefix):raiseValueError(f"_id {_id} wasn't prefixed with {self.prefix}")else:return_id[len(self.prefix):]# don't show the user the prefix# trying the store out            s=PrefixedKeyStore()s.prefix='/ROOT/'assertlist(s)==[]s['foo']='bar'# put 'bar' in 'foo'assert'foo'ins# check that 'foo' is in (i.e. a key of) sasserts['foo']=='bar'# see that the value that 'foo' contains is 'bar'assertlist(s)==['foo']# list all the keys (there's only one)assertlist(s.items())==[('foo','bar')]# list all the (key, value) pairsassertlist(s.values())==['bar']# list all the valuesassertlen(s)==1# Number of items in my stores['another']='item'# store another itemassertlen(s)==2# Now I have two!assertlist(s)==['foo','another']# here they are      

问:这一点也不令人印象深刻!和第一家店一样。这个前缀是什么意思?

A:前缀是隐藏的,这就是重点。你想说"相对的"(即"前缀自由") 语言,但在持久化数据之前,可能需要将此前缀放在键的前面 以及在显示给用户之前要删除的前缀。 考虑一下处理文件。每次存储内容时是否必须指定根文件夹 或者找回什么?

问:证明一下!

A:好的,让我们看看底层商店(dict)在处理什么:

assertlist(s.store.items())==[('/ROOT/foo','bar'),('/ROOT/another','item')]
你明白了吗?"backend"使用的键实际上是前缀"/root/"

序列化/反序列化

现在让我们演示序列化和反序列化。

假设我们想通过将"hello"附加到存储的所有内容来反序列化存储的任何文本。

# defining the storefrompy2store.baseimportStoreclassMyFunnyStore(Store):def_obj_of_data(self,data):returnf'hello {data}'# trying the store out            s=MyFunnyStore()assertlist(s)==[]s['foo']='bar'# put 'bar' in 'foo'assert'foo'ins# check that 'foo' is in (i.e. a key of) sasserts['foo']=='hello bar'# see that the value that 'foo' contains is 'bar'assertlist(s)==['foo']# list all the keys (there's only one)assertlist(s.items())==[('foo','hello bar')]# list all the (key, value) pairsassertlist(s.values())==['hello bar']# list all the values    

在下面的代码中,我们希望通过对文本进行大写来序列化文本(并将其视为这样) 当我们检索文本时。

# defining the storefrompy2store.baseimportStoreclassMyOtherFunnyStore(Store):def_data_of_obj(self,obj):returnobj.upper()# trying the store out              s=MyOtherFunnyStore()assertlist(s)==[]s['foo']='bar'# put 'bar' in 'foo'assert'foo'ins# check that 'foo' is in (i.e. a key of) sasserts['foo']=='BAR'# see that the value that 'foo' contains is 'bar'assertlist(s)==['foo']# list all the keys (there's only one)assertlist(s.items())==[('foo','BAR')]# list all the (key, value) pairsassertlist(s.values())==['BAR']# list all the values

在最后的序列化示例中,我们只实现了单向转换。 如果你只想有一个作家就可以了(所以只有N需要序列化程序)或读取器(仅此而已 需要反序列化程序)。 但是,在大多数情况下,您需要双向转换,指定对象 应该序列化以存储,以及如何反序列化以取回对象。

腌菜店

假设你想让商店充当你的序列化器。这就是它的样子。

# defining the storeimportpicklefrompy2store.baseimportStoreclassPickleStore(Store):protocol=Nonefix_imports=Trueencoding='ASCII'def_data_of_obj(self,obj):# serializerreturnpickle.dumps(obj,protocol=self.protocol,fix_imports=self.fix_imports)def_obj_of_data(self,data):# deserializerreturnpickle.loads(data,fix_imports=self.fix_imports,encoding=self.encoding)# trying the store out              s=PickleStore()assertlist(s)==[]s['foo']='bar'# put 'bar' in 'foo'asserts['foo']=='bar'# I can get 'bar' back# behind the scenes though, it's really a pickle that is stored:asserts.store['foo']==b'\x80\x03X\x03\x00\x00\x00barq\x00.'

再说一次,你能拿回一个储存在dict中的字符串,这似乎没那么令人印象深刻。 有两个原因:(1)不需要序列化字符串来存储它们;(2)不需要序列化python 对象将它们存储在dict中。 但如果您(1)试图存储更复杂的类型,并且(2)实际将它们保存在文件系统或数据库中, 然后需要序列化。 这里的要点是序列化和持久化关注点与存储和检索关注点是分开的。 代码看起来仍在使用dict。

但是你如何改变坚持者呢?

通过使用一个持久化的持久化程序。 你也可以自己写。持久化程序使用py2store所需要的是它遵循接口 python的集合.mutablemapping(或其子集)。更多关于如何让你自己坚持 您只需遵循collections.mutablemapping界面即可。

下面是一个如何在给定文件夹下的文件中持久化的简单示例。 (警告:如果您想要一个本地文件存储,不要使用它,而是在 存储文件夹!)

importosfromcollections.abcimportMutableMappingclassSimpleFilePersister(MutableMapping):"""Read/write (text or binary) data to files under a given rootdir.    Keys must be absolute file paths.    Paths that don't start with rootdir will be raise a KeyValidationError    """def__init__(self,rootdir,mode='t'):ifnotrootdir.endswith(os.path.sep):rootdir=rootdir+os.path.sepself.rootdir=rootdirassertmodein{'t','b',''},f"mode ({mode}) not valid: Must be 't' or 'b'"self.mode=modedef__getitem__(self,k):withopen(k,'r'+self.mode)asfp:data=fp.read()returndatadef__setitem__(self,k,v):withopen(k,'w'+self.mode)asfp:fp.write(v)def__delitem__(self,k):os.remove(k)def__contains__(self,k):""" Implementation of "k in self" check.        Note: MutableMapping gives you this for free, using a try/except on __getitem__,        but the following uses faster os functionality."""returnos.path.isfile(k)def__iter__(self):yield fromfilter(os.path.isfile,map(lambdax:os.path.join(self.rootdir,x),os.listdir(self.rootdir)))def__len__(self):"""Note: There's system-specific faster ways to do this."""count=0for_inself.__iter__():count+=1returncountdefclear(self):"""MutableMapping creates a 'delete all' functionality by default. Better disable it!"""raiseNotImplementedError("If you really want to do that, loop on all keys and remove them one by one.")

现在试试这个:

importos# What folder you want to use. Defaulting to the home folder. You can choose another place, but make sure rootdir=os.path.expanduser('~/')# Defaulting to the home folder. You can choose another placepersister=SimpleFilePersister(rootdir)foo_fullpath=os.path.join(rootdir,'foo')persister[foo_fullpath]='bar'# write 'bar' to a file named foo_fullpathassertpersister[foo_fullpath]=='bar'# see that you can read the contents of that file to get your 'bar' backassertfoo_fullpathinpersister# the full filepath indeed exists in (i.e. "is a key of") the persisterassertfoo_fullpathinlist(persister)# you can list all the contents of the rootdir and file foo_fullpath in it

说你自己的crud方言

不喜欢这个类似于dict的接口?想说你自己的话吗? 我们掩护你!只需子类化SimpleFilePersister并进行您想要的更改:

>>> from py2store import QuickStore
>>>
>>> store = QuickStore()  # will print what (tmp) rootdir it is choosing
>>> # Write something and then read it out again
>>> store['foo'] = 'baz'
>>> store['foo']
'baz'
>>> 
>>> # Go see that there is now a file in the rootdir, named 'foo'!
>>> 
>>> # Write something more complicated
>>> store['hello/world'] = [1, 'flew', {'over': 'a', "cuckoo's": map}]
>>> store['hello/world'] == [1, 'flew', {'over': 'a', "cuckoo's": map}]
True
>>>
>>> # do you have the key 'foo' in your store?
>>> 'foo' in store
True
>>> # how many items do you have now?
>>> assert len(store) >= 2  # can't be sure there were no elements before, so can't assert == 2
>>> 
>>> # delete the stuff you've written
>>> del store['foo']
>>> del store['hello/world']
0

变换关键点

但是处理完整路径可能会很烦人,并且可能会将代码与特定的本地系统结合得太紧密。 我们想用相对路径代替。 简单:将persister包装在前面定义的prefixedKeystore中。

>>> from py2store import QuickStore
>>>
>>> store = QuickStore()  # will print what (tmp) rootdir it is choosing
>>> # Write something and then read it out again
>>> store['foo'] = 'baz'
>>> store['foo']
'baz'
>>> 
>>> # Go see that there is now a file in the rootdir, named 'foo'!
>>> 
>>> # Write something more complicated
>>> store['hello/world'] = [1, 'flew', {'over': 'a', "cuckoo's": map}]
>>> store['hello/world'] == [1, 'flew', {'over': 'a', "cuckoo's": map}]
True
>>>
>>> # do you have the key 'foo' in your store?
>>> 'foo' in store
True
>>> # how many items do you have now?
>>> assert len(store) >= 2  # can't be sure there were no elements before, so can't assert == 2
>>> 
>>> # delete the stuff you've written
>>> del store['foo']
>>> del store['hello/world']
1

工作原理

py2store提供了三个方面,您可以定义或修改它们来存储您喜欢的内容和方式:

  • 持久性:实际存储内容的位置(内存、文件、数据库等)
  • 序列化:值转换。 python对象在持久化之前应该如何转换, 以及如何将持久化数据转换为python对象。
  • 索引:键转换。如何命名/标识/索引数据。 完整路径或相对路径。参数的唯一组合(例如(国家、城市))。<

所有这些都允许您执行诸如"将这个(值)作为那个(键)存储在那里(persitence)", 移动"in-there"的单调特性,以及"this"和"that"如何转换为适合 在那里,完全不受业务逻辑代码的影响。应该是这样的。

alt text

注意:数据的实际持久化位置取决于基本crud方法 (\u getitem\uu\u setitem\uu\u delitem\uu\u iter\uu等)将它们定义为。

您可以使用几个持久化器

我们将介绍一些基本的持久化器,它们可以随时使用。 每个类别中都有更多,我们将添加新类别,但是 这会让你开始。

这里有一个有用的函数,可以在给定键和值的情况下对存储区执行基本测试。 它并没有测试所有的存储方法(参见测试模块),而是演示 几乎每个商店都应该具备的基本功能。

>>> from py2store import QuickStore
>>>
>>> store = QuickStore()  # will print what (tmp) rootdir it is choosing
>>> # Write something and then read it out again
>>> store['foo'] = 'baz'
>>> store['foo']
'baz'
>>> 
>>> # Go see that there is now a file in the rootdir, named 'foo'!
>>> 
>>> # Write something more complicated
>>> store['hello/world'] = [1, 'flew', {'over': 'a', "cuckoo's": map}]
>>> store['hello/world'] == [1, 'flew', {'over': 'a', "cuckoo's": map}]
True
>>>
>>> # do you have the key 'foo' in your store?
>>> 'foo' in store
True
>>> # how many items do you have now?
>>> assert len(store) >= 2  # can't be sure there were no elements before, so can't assert == 2
>>> 
>>> # delete the stuff you've written
>>> del store['foo']
>>> del store['hello/world']
2

本地文件

有很多本地文件存储可供选择做。 一个通用(但不是太通用)的本地文件存储是 "py2store.stores.local_store.relativePathFormatStoreEnforcingFormat"。 它可以为你做很多事情,比如在你的键上加一个前缀(这样你就可以用相对路径而不是绝对路径来说话)。 递归列出子目录中的所有文件, 只在列出具有给定模式的文件时显示它们, 并且不允许您写入不符合模式的密钥。 此外,它还具有创建参数化路径或解析路径参数所需的功能。

>>> from py2store import QuickStore
>>>
>>> store = QuickStore()  # will print what (tmp) rootdir it is choosing
>>> # Write something and then read it out again
>>> store['foo'] = 'baz'
>>> store['foo']
'baz'
>>> 
>>> # Go see that there is now a file in the rootdir, named 'foo'!
>>> 
>>> # Write something more complicated
>>> store['hello/world'] = [1, 'flew', {'over': 'a', "cuckoo's": map}]
>>> store['hello/world'] == [1, 'flew', {'over': 'a', "cuckoo's": map}]
True
>>>
>>> # do you have the key 'foo' in your store?
>>> 'foo' in store
True
>>> # how many items do you have now?
>>> assert len(store) >= 2  # can't be sure there were no elements before, so can't assert == 2
>>> 
>>> # delete the stuff you've written
>>> del store['foo']
>>> del store['hello/world']
3

localfilestore的签名是:

>>> from py2store import QuickStore
>>>
>>> store = QuickStore()  # will print what (tmp) rootdir it is choosing
>>> # Write something and then read it out again
>>> store['foo'] = 'baz'
>>> store['foo']
'baz'
>>> 
>>> # Go see that there is now a file in the rootdir, named 'foo'!
>>> 
>>> # Write something more complicated
>>> store['hello/world'] = [1, 'flew', {'over': 'a', "cuckoo's": map}]
>>> store['hello/world'] == [1, 'flew', {'over': 'a', "cuckoo's": map}]
True
>>>
>>> # do you have the key 'foo' in your store?
>>> 'foo' in store
True
>>> # how many items do you have now?
>>> assert len(store) >= 2  # can't be sure there were no elements before, so can't assert == 2
>>> 
>>> # delete the stuff you've written
>>> del store['foo']
>>> del store['hello/world']
4

通常path_格式只用于指定rootdir,如上所述。 但您可以进一步指定所需的格式。 例如,下面只会产生.wav文件, 只允许您写入以.wav结尾的键:

>>> from py2store import QuickStore
>>>
>>> store = QuickStore()  # will print what (tmp) rootdir it is choosing
>>> # Write something and then read it out again
>>> store['foo'] = 'baz'
>>> store['foo']
'baz'
>>> 
>>> # Go see that there is now a file in the rootdir, named 'foo'!
>>> 
>>> # Write something more complicated
>>> store['hello/world'] = [1, 'flew', {'over': 'a', "cuckoo's": map}]
>>> store['hello/world'] == [1, 'flew', {'over': 'a', "cuckoo's": map}]
True
>>>
>>> # do you have the key 'foo' in your store?
>>> 'foo' in store
True
>>> # how many items do you have now?
>>> assert len(store) >= 2  # can't be sure there were no elements before, so can't assert == 2
>>> 
>>> # delete the stuff you've written
>>> del store['foo']
>>> del store['hello/world']
5

下面将添加这些.wav文件具有"somestring"格式的限制 后跟数字:

>>> from py2store import QuickStore
>>>
>>> store = QuickStore()  # will print what (tmp) rootdir it is choosing
>>> # Write something and then read it out again
>>> store['foo'] = 'baz'
>>> store['foo']
'baz'
>>> 
>>> # Go see that there is now a file in the rootdir, named 'foo'!
>>> 
>>> # Write something more complicated
>>> store['hello/world'] = [1, 'flew', {'over': 'a', "cuckoo's": map}]
>>> store['hello/world'] == [1, 'flew', {'over': 'a', "cuckoo's": map}]
True
>>>
>>> # do you have the key 'foo' in your store?
>>> 'foo' in store
True
>>> # how many items do you have now?
>>> assert len(store) >= 2  # can't be sure there were no elements before, so can't assert == 2
>>> 
>>> # delete the stuff you've written
>>> del store['foo']
>>> del store['hello/world']
6

你明白了…

localfilestore的其他参数或python的open函数的其他参数。 稍有不同的是,这里的模式参数同时适用于读和写。 例如,如果mode='b'打开文件时,将使用mode='rb'读取 打开写入时使用mode='wb'。对于非对称读/写模式, 用户可以指定读取模式写入模式(在这种情况下,忽略模式参数)。

MongoDB

MongoDB集合不像文件系统那样自然地成为键值存储。 mongodb存储"documents",它们是数据的json,有许多(可能是嵌套的)字段不是 默认情况下由架构强制。因此,为了将mongo作为一个密钥值存储库,我们需要 指定哪些字段应视为键,哪些字段应视为数据。

默认情况下,\u id字段(默认情况下唯一确保包含唯一值的字段)是单键字段,并且 所有其他字段都被视为数据字段。

>>> from py2store import QuickStore
>>>
>>> store = QuickStore()  # will print what (tmp) rootdir it is choosing
>>> # Write something and then read it out again
>>> store['foo'] = 'baz'
>>> store['foo']
'baz'
>>> 
>>> # Go see that there is now a file in the rootdir, named 'foo'!
>>> 
>>> # Write something more complicated
>>> store['hello/world'] = [1, 'flew', {'over': 'a', "cuckoo's": map}]
>>> store['hello/world'] == [1, 'flew', {'over': 'a', "cuckoo's": map}]
True
>>>
>>> # do you have the key 'foo' in your store?
>>> 'foo' in store
True
>>> # how many items do you have now?
>>> assert len(store) >= 2  # can't be sure there were no elements before, so can't assert == 2
>>> 
>>> # delete the stuff you've written
>>> del store['foo']
>>> del store['hello/world']
7

但是,每次都把密钥指定为dict会让人恼火。 键模式是固定的,因此您应该能够指定生成键的值的元组。 您可以使用mongotuplekeystore

>>> from py2store import QuickStore
>>>
>>> store = QuickStore()  # will print what (tmp) rootdir it is choosing
>>> # Write something and then read it out again
>>> store['foo'] = 'baz'
>>> store['foo']
'baz'
>>> 
>>> # Go see that there is now a file in the rootdir, named 'foo'!
>>> 
>>> # Write something more complicated
>>> store['hello/world'] = [1, 'flew', {'over': 'a', "cuckoo's": map}]
>>> store['hello/world'] == [1, 'flew', {'over': 'a', "cuckoo's": map}]
True
>>>
>>> # do you have the key 'foo' in your store?
>>> 'foo' in store
True
>>> # how many items do you have now?
>>> assert len(store) >= 2  # can't be sure there were no elements before, so can't assert == 2
>>> 
>>> # delete the stuff you've written
>>> del store['foo']
>>> del store['hello/world']
8

S3

它的工作方式与localstores非常相似,但存储在s3中。你需要在 我们可以用这个。在py2store.stores.s3商店中查找s3商店。

用例

接口读取

有多少次有人以某些嵌套文件夹的zip格式与您共享某些数据 谁的结构和命名选择令人着迷地晦涩难懂?然后你要花多少时间来编写代码 和那个怪胎接触?好吧,py2store的目的之一就是让这件事变得更容易。 您仍然需要了解数据存储的结构,以及如何将这些数据反序列化为python。 可以操纵的对象。但如果有了合适的工具,你不必做更多的事情。

更改存储内容的位置和方式

必须切换持久化对象的位置(比如从文件系统切换到s3),或者更改数据键的方式, 或者数据序列化的方式?如果使用py2store工具来分离不同的存储问题, 这将很容易改变,因为改变将是本地化的。如果你处理的代码 写的,考虑到所有的问题,py2store应该仍然能够提供帮助,因为您可以 更容易给新系统一个外观,使其看起来像旧系统。

所有这些也可以应用到数据库中,只要您使用的是crud操作 基本方法涵盖。

适配器:当学习曲线妨碍学习时

新的存储机制(dbs等)不断诞生,一些人开始使用它们,我们是eventu联盟领导使用它们 如果我们需要和那些人的系统一起工作。尽管我们很想学习 新来的孩子们的能力,有时我们没有时间去做。

如果有人给我们熟悉的新系统写了一个适配器,这不是很好吗? 像mongo一样与sql对话(反之亦然)。像文件系统一样与s3交谈。 现在这不是一个长期的解决方案:如果我们真的要大量使用新系统,我们 应该学会。但是当你刚开始做事的时候 是救命稻草。

py2store希望让您能够更轻松地推出适配器 以您熟悉的方式进入新系统。

以后再考虑存储,如果有的话

您有一个新项目或需要编写一个新应用程序。你需要储存资料并把资料读回来。 内容:你的应用需要运行的不同类型的资源。有些人喜欢思考 如何优化这方面。我没有。到时候我会交给专家来做。 不过,如果有的话,时间往往会晚些。很少有概念和MVP的证明能够成功生产。

因此,我只想继续学习业务逻辑并编写我的程序。 所以我需要一个简单的方法来获得一些最小的存储功能。 但是到了优化的时候,我不应该改变我的代码,而是改变我 道做事。我需要的是py2存储。

商店是虫子吗?刀?< >

你想怎么叫就怎么叫吧,真的。

将py2store转换为ya(p)orm(又一个(python)对象关系映射)是很有诱惑力的, 但那将是误导。py2store的目的不是将对象映射到数据库条目, 而是为基本的存储操作提供一致的接口。

从这个意义上说,py2store更类似于数据访问对象(dao)模式的实现。 当然,orm和dao之间的区别可能很模糊,所以所有这些都应该加上一点盐。

这种抽象的优点和缺点易于搜索和查找。

大多数数据交互机制都可以通过collections.abc接口的子集来满足。 例如,可以将python的collections.mapping接口用于任何键值存储,使数据访问 对象具有dict的外观,而不是使用其他流行的方法名选项,例如 例如读/写、加载/转储等。 其中一个危险是,由于dao的外观和行为类似于dict(但不是),用户可能会低估它 一些操作的运行成本。

一些链接

orm:对象关系映射:https://en.wikipedia.org/wiki/object-relational\u mapping

dao:数据访问对象:https://en.wikipedia.org/wiki/data-access-object" rel="nofollow">https://en.wikipedia.org/wiki/data-access-object

欢迎加入QQ群-->: 979659372 Python中文网_新手群

推荐PyPI第三方库


热门话题
java log4j找不到log4jtest。房产?   我在java线程“awteventque1”中获得异常。lang.NullPointerException   java为什么在使用完整路径从文件系统读取文件时出错?   java如何迭代所有注册表项?   java中的安卓 Opencv SVM未正确训练   多线程Java ThreadPoolExecutor关闭特定线程?   如何使用Java NIO CreateDirectory方法设置目录所有者组?   java NatTable混合了固定宽度的列和可调整大小的填充剩余空间   java如何删除特定网络,即使该网络是由安卓上的其他设备创建的?   java Guava toJavaUtil()不存在   java对许多常量使用枚举是有效的memorywise?   java是否可以使用坐标定位JButton?   从WSDL生成java代码导致异常   java如何在安卓中导出javadoc   爬行JAX中的java NoClassDefFoundError错误   java为片段中的文本视图设置区域设置   发送最后一条消息后发生Java RMI RemoteException