1

背景: 假设我们有一个打开常用数据库连接的函数,基本上类似于以下内容,但有额外的花里胡哨:

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)返回两个连接而不是一个,或者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模块。” 这就是我所做的,只是我不太清楚它是如何工作的......

>>> import copy
>>> a = (i for i in [1,2])
>>> a
<generator object <genexpr> at 0x03FB0878>
>>> copy.copy(a)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "C:\WinPython-32bit-2.7.5.1\python-2.7.5\lib\copy.py", line 96, in copy
    return _reconstruct(x, rv, 0)
  File "C:\WinPython-32bit-2.7.5.1\python-2.7.5\lib\copy.py", line 329, in _reconstruct
    y = callable(*args)
  File "C:\WinPython-32bit-2.7.5.1\python-2.7.5\lib\copy_reg.py", line 93, in __newobj__
    return cls.__new__(cls, *args)
TypeError: object.__new__(generator) is not safe, use generator.__new__()
>>> copy.deepcopy(a)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "C:\WinPython-32bit-2.7.5.1\python-2.7.5\lib\copy.py", line 190, in deepcopy
    y = _reconstruct(x, rv, 1, memo)
  File "C:\WinPython-32bit-2.7.5.1\python-2.7.5\lib\copy.py", line 329, in _reconstruct
    y = callable(*args)
  File "C:\WinPython-32bit-2.7.5.1\python-2.7.5\lib\copy_reg.py", line 93, in __newobj__
    return cls.__new__(cls, *args)
TypeError: object.__new__(generator) is not safe, use generator.__new__()

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

4

2 回答 2

2

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

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

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

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

于 2013-07-19T18:59:34.757 回答
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(可怕的名字..)然后扩展它来变得更好。

于 2013-07-19T18:29:01.527 回答