Python 3.x 在Python 3.0中向类动态添加方法

Python 3.x 在Python 3.0中向类动态添加方法,python-3.x,python,Python 3.x,Python,我试图用Python编写一个数据库抽象层,它允许您使用链式函数调用构造SQL语句,例如: results = db.search("book") .author("J. K. Rowling") .price("<40.00") .title("Harry") .execute() 但是,当我运行示例时,例如: print(db.search("book").price(">4.00").execute

我试图用Python编写一个数据库抽象层,它允许您使用链式函数调用构造SQL语句,例如:

results = db.search("book")
          .author("J. K. Rowling")
          .price("<40.00")
          .title("Harry")
          .execute()
但是,当我运行示例时,例如:

print(db.search("book").price(">4.00").execute())
产出:

{'__Set__': 'harry'}

我走错方向了吗?有没有更好的方法来获取被调用函数的名称或以某种方式制作函数的“硬拷贝”呢?

以下是一些可以让您开始使用的工作代码(不是您试图编写的整个程序,而是一些可以展示各部分如何组合在一起的代码):


首先,您没有向类中添加任何内容,而是将其添加到实例中

其次,您不需要访问dict
self.\uuuu dict\uuuuu[opt]=self.\uuuu Set\uuuuu
最好使用
setattr(self,opt,self.\uu Set\uuu)
完成

第三,不要使用
\uuuuxxx\uuuuuxx
作为属性名。这些是为Python内部使用而保留的

第四,正如您所注意到的,Python不容易被愚弄。您调用的方法的内部名称仍然是
\uuuu Set\uuu
,即使您使用不同的名称访问它。:-)当您将方法定义为
def
语句的一部分时,将设置该名称


您可能希望使用元类创建和设置options方法。您可能还希望实际创建这些方法,而不是尝试对所有方法使用一个方法。如果你真的只想使用一个
\uuu getattr\uuuu
是一种方法,但是它可能有点不方便,我通常不建议这样做。lambda或其他动态生成的方法可能更好。

您只需在创建类后添加搜索函数(方法)

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:该类有一个
\uuu init\uuu()
方法,该方法没有
参数(条件设置器在运行时动态添加,但添加到类中,而不是单独添加到每个实例中)。如果需要创建具有不同条件设置器的
Search
对象,则上述方法的以下变体有效(
\uuuuu init\uuuu()
方法具有
参数):

参考:


如果您确实需要知道存储它们的属性名称的搜索方法,只需在
make\u set\u condition()
中使用

       set_cond.__name__ = option  # Sets the function name
(就在返回设置条件之前)。执行此操作之前,方法
Search.name
具有以下名称:

>>> Search.price
<function set_cond at 0x107f832f8>

以这种方式设置方法名称可以使涉及该方法的可能错误消息更容易理解。

请问您为什么要尝试重新发明SQLAlchemy?事实上,我经常尝试编写自己的库,这些库复制了已经存在的库,以便我能更多地了解该语言,更好地了解如何使用更多的方法高级部分汇集在一起:)好的,一个学习练习,这是一个很好的理由。虽然在这种情况下,我认为阅读SQLAlchemy源代码将是一个良好的开端。ORM是复杂而神奇的。你说得对。我太聪明了。这些条件设置器方法要求在每个实例上创建它们,这并没有什么特别之处。我假设这些方法将从架构自动设置到何处,可能我假设的太多了。:-)根据问题描述,方法名称将因族而异,因此在类上设置名称将导致某些族的名称丢失,或每个族的所有名称都丢失。@EthanFurman:你说得对。我猜测(可能是错误的)该族实际上不是一个合适的
\uuuu init\uuuu()
参数,因为在给定的程序中,
\uuu init\uuuu()
总是使用同一个族调用。我添加了一个PS来强调这一点,并给出了一个解决方案,使每个
搜索
实例都有自己的一系列可能的选项搜索。以下是关于
\uu*\ uuu
名称的参考:@EOL我一直认为“xxx”名称是“私有”属性或类的方法,不能从外部调用?有什么约定是你应该使用的吗?@Ben:是的,有一个约定,它与Python保留的符号非常相似:它是
\uuuxxx
(没有尾随的“dunder”)–与上面的引用相同。@EOL:不,这不是约定,这是引发名称混乱的原因。不同。@Ben:将某事物标记为内部的约定是以一个下划线开头。有两个下划线会触发名称混乱,这有助于避免命名冲突。每次搜索都会创建一个
Assign
实例,这会占用大量的时间和内存。:)与我在回答中概述的直接“创建类后添加方法”方法相比,这种方法有什么优势吗?@EOL这种方法就是Python本身的工作方式,也是一种常见的习惯用法。例如,每次进行方法调用(如
a.m(x)
)时,Python都会创建一个新的BoundMethod实例。OP试图学习Python是如何工作的,对于调用和动态点查找,教授uu-call和u-getattr是合适的。这就是他们的目的。谢谢。是的,
\uuuuuu getattr\uuuuu
\uuuu call\uuuuuuu
在动态查找方面确实很重要。我不确定OP需要什么:我知道他想要定义非动态类属性,而不是动态实例属性。不(我认为,这大致概括了我们在回答问题时采取的两种不同方法。)
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']
       set_cond.__name__ = option  # Sets the function name
>>> Search.price
<function set_cond at 0x107f832f8>
>>> Search.price
<function price at 0x107f83490>