与Python的流畅接口

与Python的流畅接口,python,dsl,fluent-interface,Python,Dsl,Fluent Interface,我有一个Python函数“send_message”,它包含三个参数: send_message("i like windmills", to="INBOX", from="OUTBOX") 我正在考虑在上面加一个流畅的界面。理想情况下,我想写以下任何一项: send_message("i like windmills").to("INBOX").from("OUTBOX")

我有一个Python函数“send_message”,它包含三个参数:

send_message("i like windmills", to="INBOX", from="OUTBOX")
我正在考虑在上面加一个流畅的界面。理想情况下,我想写以下任何一项:

send_message("i like windmills").to("INBOX").from("OUTBOX")

send_message("i like windmills").from("OUTBOX").to("INBOX")

# The `to()` information is mandatory but the `from()` is not (as with real letters), so this one would also be a valid call:
send_message("i like windmills").to("INBOX")
有什么办法可以做到这一点或类似的吗

我理解对象返回“自我”的方法的一般方法,但在我的理解中,这将导致如下结果:

message = Message("i like windmills")
message.to("INBOX").from("OUTBOX").send()
但是这一个并不像前面的例子那么好,我实际上更喜欢带有命名参数的原始版本


非常感谢您的帮助。

这可以通过这种方式完成,我不确定是否有更好的方式,因为这是我的第一次尝试。祝你好运

DEFAULT_SENDER = 'my_address'
#Because the sender object is optional I assume you have a default sender

class Send_message(object):
    def __init__(self, message):
        self.message = message
        self.sender = None
        self.receiver = None
        self.method = None

    def to(self, receiver):
        self.receiver = receiver
        self.method = self.send()
        return self

    def _from(self, sender):
        self.sender = sender
        self.method = self.send()
        return self

    def __call__(self):
        if self.method:
            return self.method()
        return None

    def send(self):
        if self.receiver:
            if not self.sender:
                self.sender = DEFAULT_SENDER

            return lambda:actual_message_code(self.message, self.sender, self.receiver)


def actual_message_code(message, sender, receiver):
    print "Sent '{}' from: {} to {}.".format(message, sender, receiver)



Send_message("Hello")._from('TheLazyScripter').to('samba2')()
Send_message("Hello").to('samba2')._from('TheLazyScripter')()
Send_message("Hello").to('samba2')()

#Only change in actual calling is the trailing ()

通过实现
\uuuuu call\uuuuu
方法,我们可以知道何时处于调用链的末尾。这当然会添加后面的
()
调用。并要求您更改指向实际消息传递方法和默认发送者变量的指针,但我认为这是实现目标的最简单方法,而不需要实际知道链何时结束

返回
self
有一个缺点。改变其中一个变量可能会影响另一个变量。这里有一个例子

取自@TheLazyScript代码,但示例已修改

a = Send_message("Hello")
b = a
a = a._from('theLazyscripter')
b = b._from('Kracekumar').to('samba 2')
b()
a.message = 'Hello A'
a.to('samba2')()
b.to('samba 2')()
Send_message("Hello").to('samba2')._from('TheLazyScripter')()
Send_message("Hello").to('samba2')()
a和b变量指向同一个实例。修改一个值将影响其他值。请参见输出的第二行和第三行

输出

Sent 'Hello' from: Kracekumar to samba 2.
Sent 'Hello A' from: Kracekumar to samba2.
Sent 'Hello A' from: Kracekumar to samba 2.
Sent 'Hello' from: TheLazyScripter to samba2.
Sent 'Hello' from: my_address to samba2.
Sent 'Hello' from: Kracekumar to samba 2.
Sent 'Hello A' from: theLazyscripter to samba2.
Sent 'Hello' from: Kracekumar to samba 2.
Sent 'Hello' from: TheLazyScripter to samba2.
Sent 'Hello' from: my_address to samba2.
b=a
并且修改
a
的内容会影响
b
的值

如何消除这种副作用?

而不是返回
self
返回一个新实例以消除副作用

DEFAULT_SENDER = 'my_address'
#Because the sender object is optional I assume you have a default sender

class Send_message(object):
    def __init__(self, message):
        self.message = message
        self.sender = None
        self.receiver = None
        self.method = None

    def _clone(self):
        inst = self.__class__(message=self.message)
        inst.sender = self.sender
        inst.receiver = self.receiver
        inst.method = self.method
        return inst

    def to(self, receiver):
        self.receiver = receiver
        self.method = self.send()
        return self._clone()

    def _from(self, sender):
        self.sender = sender
        self.method = self.send()
        return self._clone()

    def __call__(self):
        if self.method:
            return self.method()
        return None

    def send(self):
        if self.receiver:
            if not self.sender:
                self.sender = DEFAULT_SENDER

        return lambda:actual_message_code(self.message, self.sender, self.receiver)


def actual_message_code(message, sender, receiver):
    print("Sent '{}' from: {} to {}.".format(message, sender, receiver))



a = Send_message("Hello")
b = a
a = a._from('theLazyscripter')
b = b._from('Kracekumar').to('samba 2')
b()
a.message = 'Hello A'
a.to('samba2')()
b.to('samba 2')()
Send_message("Hello").to('samba2')._from('TheLazyScripter')()
Send_message("Hello").to('samba2')()
\u clone
方法每次都会创建实例的新副本。注意:当其中一个值是列表或字典时,需要调用深度副本。这里是字符串,因此不需要。但想法是一样的,在返回之前复制每个属性

输出

Sent 'Hello' from: Kracekumar to samba 2.
Sent 'Hello A' from: Kracekumar to samba2.
Sent 'Hello A' from: Kracekumar to samba 2.
Sent 'Hello' from: TheLazyScripter to samba2.
Sent 'Hello' from: my_address to samba2.
Sent 'Hello' from: Kracekumar to samba 2.
Sent 'Hello A' from: theLazyscripter to samba2.
Sent 'Hello' from: Kracekumar to samba 2.
Sent 'Hello' from: TheLazyScripter to samba2.
Sent 'Hello' from: my_address to samba2.
输出行号
2
3
清楚地表明新代码中没有副作用


我写了一篇关于

您想要实现的博客文章
send_message
将是创建生成器的工厂方法;它避免了对象“开销”,这显然不是OP.Hi Lutz所希望的,也不是完全。生成器模式需要链中的最后一个“build()”方法来实际执行操作。我希望找到一种方法,使链中的每个方法都能判断它是否是最后一个(通过元编程?)。如果是这种情况,它实际上会隐式执行操作。@samba2:那么请在您的问题中编辑这与生成器模式有何不同。我不明白
message.to(“收件箱”).from(“发件箱”).send()
如何不像前面的示例那样“好”,我想你的意思是不应该有一个尾随的
/build()/send()/whatever()
调用,每个方法调用都应该自动判断它是否是链中的最后一个,如果是,则触发一个
.send()
。对我来说,这听起来不太理想,也很危险,因为现在你不能做多个任务,比如从(“发件箱”)到(“雪莉”),如果第一个任务没有触发广泛的
.send()
,那么你就不能完成多个任务。而且它强制所有流畅的代码行都是一行,不受行长限制,不能跨行分割复杂的代码。嗨,懒惰的编剧。我把你的答案投了赞成票,因为它是我最初问题的一个非常简单的解决方案。然而,随着敏捷需求的发展。您向我指出了我原来的问题中缺少的一个事实:
to()
方法是强制性的,而
from()
方法是可选的。由于您的想法是基于“发送者”和“接收者”这两个属性的呈现,我认为它不会起作用。我已经更新了我的代码以适应您更新的参数。