Python中的通用函数/对象加倍装饰器是否可行?

时间:2013-07-19 16:51:53

标签: python object copy decorator

背景 假设我们有一个函数可以打开一个经常使用的数据库连接,这个函数基本上类似于以下内容,但有额外的功能和口哨:

import getpass

import MySQLdb

def myspecialconnect(user='foo', host='bar', port=80085):
    password = getpass.getpassword('Enter your password: ')
    return MySQLdb.connect(user, password, host, port)

也许有时,我们想要打开两个连接:

read_connection = myspecialconnect()
write_connection = myspecialconnect()

多么痛苦 - 我需要输入两次密码,当我想要的只是同样的事情时。当然,有很多方法可以修改这个示例以避免 - 例如 ,可以像myspecialconnect(multi=True)一样添加一个参数来返回两个连接而不是一个,或{{1}如果你想变得疯狂,用相应的代码来实现这一功能。然而,这个特殊情况促使我想知道更普遍的应用。

问题:如果我想从任意函数中获得此功能(返回我们想要的任何内容的多个副本)该怎么办?嗯 - 这可能很棘手。

首先,为了确认它不起作用,我尝试了这个:

myspecialconnect(copies=9)

对于不需要用户输入的功能,这是可以的;否则,你仍然必须坐在那里并连续两次输入完全相同的东西。这很容易解决,但到现在你可以看到它的发展方向:

def doubled(function):
    def Wrapper(*args, **kwargs):
        return (function(*args, **kwargs),function(*args, **kwargs))
    return Wrapper

这个版本只接受一次用户输入,但它返回两次相同的引用,只不过是一种不必要的复杂方式def doubled(function): def Wrapper(*args, **kwargs): result = function(*args, **kwargs) return (result, result) return Wrapper 。 “啊哈!”我说,“也许我应该看看foo = bar = object()模块。”这就是我做的,只是我不太清楚它是如何工作的......

copy

当然,到目前为止,我已经花了相当多的时间来证明(或更多)这个小问题,这意味着我非常好奇。这可以通过返回任意实例的副本的方式完成,而不会变成一个被迫明确处理几十个案例的怪物,每个案例都以他们自己的特殊方式进行吗?

2 个答案:

答案 0 :(得分:2)

没有一般方法可以做你想要的。如果你只想重播第一个函数调用就很简单 - 你的第一次尝试就行了。不幸的是,重放用户输入的要求使事情变得复杂。

首先,您不需要副本。你会如何复制数据库连接?在网络连接的另一端有状态,你必须复制,你必须选择新的端口,并且它不会真正成为一个副本,具有相同的状态和属性。您希望使用与旧参数相同的参数打开新连接。

其次,装饰者无法知道要重放的输入。使用相同的参数调用函数很容易。调用一个函数两次,将用户输入从第一次调用重放到第二次调用,是很麻烦但可能。但是,如果装饰器试图将第一次调用中的所有输入重放到第二次调用中,那么它最终也会重放数据库的TCP响应。第二次调用将与装饰器通信并返回不起作用的连接对象,而不是与数据库通信并建立连接。

不要试图加倍myspecialconnect,而是创建一个不需要读取用户输入并将其加倍的函数。读取密码一次,然后将其传递给双重功能。

答案 1 :(得分:0)

编辑:

如果我理解你的问题以及一个例子,这是一个通用的解决方案。

generator = None

def getObject(askOnce = None, createFunction = None, *args):
    global generator
    if generator is None:
        askOnceValue = askOnce()
        generator = getGenerator(askOnceValue, createFunction, *args)
    return generator.next()

def getGenerator(askOnceValue, createFunction, *args):
    while(True):
        yield createFunction(askOnceValue, *args)

def myInputFunction():
    return "Some dynamic value"

def myCreateFunction(myInput, arg1, arg2):
    return list([myInput, arg1, arg2])

myObj1 = getObject(myInputFunction, myCreateFunction, "hello", 1234)

print(myObj1)

myObj2 = getObject()

print(myObj2)

通过创建一个类CopyableObject(可怕的名称..)然后扩展它可以使这更好。