我正在尝试在Python中编写数据库抽象层,它允许您使用链式函数调用来构造SQL语句,例如:
results = db.search("book")
.author("J. K. Rowling")
.price("<40.00")
.title("Harry")
.execute()
但是当我尝试将所需方法动态添加到db类时,我遇到了问题。
以下是我的代码的重要部分:
import inspect
def myName():
return inspect.stack()[1][3]
class Search():
def __init__(self, family):
self.family = family
self.options = ['price', 'name', 'author', 'genre']
#self.options is generated based on family, but this is an example
for opt in self.options:
self.__dict__[opt] = self.__Set__
self.conditions = {}
def __Set__(self, value):
self.conditions[myName()] = value
return self
def execute(self):
return self.conditions
但是,当我运行例如:
时print(db.search("book").price(">4.00").execute())
输出:
{'__Set__': 'harry'}
我是以错误的方式来做这件事的吗?有没有更好的方法来获取被调用函数的名称或以某种方式制作函数的“硬拷贝”?
答案 0 :(得分:9)
您可以在创建课程后添加搜索功能(方法) :
class Search: # The class does not include the search methods, at first
def __init__(self):
self.conditions = {}
def make_set_condition(option): # Factory function that generates a "condition setter" for "option"
def set_cond(self, value):
self.conditions[option] = value
return self
return set_cond
for option in ('price', 'name'): # The class is extended with additional condition setters
setattr(Search, option, make_set_condition(option))
Search().name("Nice name").price('$3').conditions # Example
{'price': '$3', 'name': 'Nice name'}
PS :此类有__init__()
方法,没有family
参数(条件设置器在运行时动态添加,但会添加到类中,不要分别对每个实例)。如果需要创建具有不同条件设置器的<{1}} 对象,则上述方法的以下变体有效(Search
方法具有__init__()
参数):
family
参考:http://docs.python.org/howto/descriptor.html#functions-and-methods
如果您确实需要了解其存储属性名称的搜索方法,您可以使用
在import types
class Search: # The class does not include the search methods, at first
def __init__(self, family):
self.conditions = {}
for option in family: # The class is extended with additional condition setters
# The new 'option' attributes must be methods, not regular functions:
setattr(self, option, types.MethodType(make_set_condition(option), self))
def make_set_condition(option): # Factory function that generates a "condition setter" for "option"
def set_cond(self, value):
self.conditions[option] = value
return self
return set_cond
>>> o0 = Search(('price', 'name')) # Example
>>> o0.name("Nice name").price('$3').conditions
{'price': '$3', 'name': 'Nice name'}
>>> dir(o0) # Each Search object has its own condition setters (here: name and price)
['__doc__', '__init__', '__module__', 'conditions', 'name', 'price']
>>> o1 = Search(('director', 'style'))
>>> o1.director("Louis L").conditions # New method name
{'director': 'Louis L'}
>>> dir(o1) # Each Search object has its own condition setters (here: director and style)
['__doc__', '__init__', '__module__', 'conditions', 'director', 'style']
中进行设置。
make_set_condition()
(就在 set_cond.__name__ = option # Sets the function name
之前)。在执行此操作之前,方法return set_cond
具有以下名称:
Search.name
设置其>>> Search.price
<function set_cond at 0x107f832f8>
属性后,您会得到一个不同的名称:
__name__
以这种方式设置方法名称使得涉及该方法的错误消息更容易理解。
答案 1 :(得分:4)
首先,您没有向类中添加任何内容,而是将其添加到实例中。
其次,您无需访问 dict 。使用self.__dict__[opt] = self.__Set__
可以更好地完成setattr(self, opt, self.__Set__)
。
第三,不要使用__xxx__
作为属性名称。这些保留用于Python内部使用。
第四,正如您所注意到的,Python不容易被愚弄。您调用的方法的内部名称仍为__Set__
,即使您使用其他名称访问它。 :-)当您将方法定义为def
语句的一部分时,将设置名称。
您可能希望使用元类创建和设置选项方法。您也可能希望实际创建这些方法,而不是尝试为所有方法使用一种方法。如果你真的只想使用一个__getattr__
的方式,但它可能有点繁琐,我通常建议反对它。 Lambda或其他动态生成的方法可能更好。
答案 2 :(得分:3)
这里有一些工作代码可以帮助您入门(不是您尝试编写的整个程序,而是显示部件如何组合在一起的东西):
class Assign:
def __init__(self, searchobj, key):
self.searchobj = searchobj
self.key = key
def __call__(self, value):
self.searchobj.conditions[self.key] = value
return self.searchobj
class Book():
def __init__(self, family):
self.family = family
self.options = ['price', 'name', 'author', 'genre']
self.conditions = {}
def __getattr__(self, key):
if key in self.options:
return Assign(self, key)
raise RuntimeError('There is no option for: %s' % key)
def execute(self):
# XXX do something with the conditions.
return self.conditions
b = Book('book')
print(b.price(">4.00").author('J. K. Rowling').execute())