【问题标题】:Decorator isn't passing arguments?装饰器不传递参数?
【发布时间】:2012-06-13 14:01:53
【问题描述】:

我一直致力于将 n 维数组序列化为一维“数组”数据库:

from collections import Iterable, Mapping
import sqlite3

def pass_many(vals, some_funct, args=()):
    if not vals:
        return
    if isinstance(vals, Iterable) and not isinstance(vals, (basestring, Mapping)):
        for v in vals:
            pass_many(v, some_funct, args)
    else:
        some_funct(vals, *args)

def counter(func):
    def wrapper(v, *args, **kwargs): # added 'v' arg to no avail
        wrapper.count = wrapper.count + 1
        test_var_args(v, *args, **kwargs)
        #return func(*args, **kwargs)
    wrapper.count = 0

    return wrapper

def test_var_args(farg, *args):
    print "formal arg:", farg
    for arg in args:
        print "another arg:", arg

@counter
def insert(val, cursor, table="wordlist", logfile="queries.log"):
    print val, cursor, table, logfile
    if val:
        if isinstance(val, (basestring, Mapping)):
            val = '\"' + val + '\"'
        else: val = str(val)
        query = "insert into tablename values (?);".replace('tablename', table).replace('?', val)
        #if logfile: to_logfile(query + '\n', logfile)
        cursor.execute(query)

if __name__ == '__main__':
    connection = sqlite3.connect('andthensome.db')
    cursor = connection.cursor()
    cursor.execute("create table array (word text);")
    pass_many([["foo", "bar"], "pew"], insert, cursor)
    connection.commit()
    cursor.execute("select * from array;") # wrapped select function omitted for brevity
    print "insert() was called", insert.count, "times, and db now contains:\n", cursor.fetchall()
    cursor.close()

输出:

formal arg: foo
formal arg: bar
formal arg: pew
insert() was called 3 times, and db now contains:
[]

输出取消注释#return func(*args, **kwargs):

formal arg: foo
Traceback (most recent call last):
    Line 42, in <module>
        pass_many([["foo", "bar"], "pew"], insert, cursor)
    Line 9, in pass_many
        pass_many(v, some_funct, args)
    Line 9, in pass_many
        pass_many(v, some_funct, args)
    Line 11, in pass_many
        some_funct(vals, *args)
    Line 17, in wrapper
        return func(*args, **kwargs)
    TypeError: insert() takes at least 2 arguments (0 given)

预期输出(省略调试功能):

insert() was called 3 times, and db now contains:
["foo","bar","pew"]

不幸的是,用counter 装饰的insert 函数似乎没有正确传递参数。

我做错了什么?

【问题讨论】:

  • 装饰器中的函数调用是否有原因被注释掉了?
  • 是的,这样调试test_var_args函数就可以一直运行insert来显示什么时候出现的args。
  • 你能提供一个更简单的测试用例吗?在一个简单的无操作函数 (def func(*args, **kwargs): pass) 上使用装饰器显示问题的方法是理想的。此外,最好有问题的确切细节 - 即,您期望的输出和实际得到的输出的列表。
  • 你怎么知道它不起作用?你期望什么输出,你得到什么?如果您收到错误,那是什么?
  • 更新了当前输出,输出取消注释return func(*args, **kwargs) 和预期输出。

标签: python sqlite functional-programming decorator pysqlite


【解决方案1】:

一个问题似乎是您正在使用*args 来扩展您的args 参数,但是您将cursor 作为该参数的值传入而没有将其包装在一个元组中。因此,当您似乎希望它是 insert("foo", cursor) 时,您的最终呼叫是 insert("foo", *cursor)。试试pass_many([["foo", "bar"], "pew"], insert, (cursor,))

我认为正在发生的事情是,当您这样做时,您的 test_var_args 函数正在消耗光标对象(这显然是可迭代的),因此在随后调用真正的 insert 时不再留下更多参数来扩展功能。

在您回复后编辑:您真的不想将v 传递给您的func 电话吗?你写的插入函数有两个参数,vcursor,但你只用cursor 调用它。你为什么要包装insert?额外的参数应该做什么?你没有在包装器中使用它,也没有将它传递给底层函数,那么它的目的是什么?

【讨论】:

  • 谢谢,给了我更好的结果:TypeError: insert() takes at least 2 arguments (1 given)
【解决方案2】:

根据@BrenBarn 的建议,我已将其修改为:

def counter(func):
    def wrapper(v, *args, **kwargs):
        wrapper.count = wrapper.count + 1
        test_var_args(v, *args, **kwargs)
        return func(v, *args, **kwargs)
    wrapper.count = 0

    return wrapper

pass_many([["foo", "bar"], "pew"], insert, [cursor])

【讨论】:

    【解决方案3】:

    有几件事:首先,在wrapper()

    def counter(func):
        def wrapper(v, *args, **kwargs): # added 'v' arg to no avail
            wrapper.count = wrapper.count + 1
            test_var_args(v, *args, **kwargs)
            return func(*args, **kwargs)
        ...
    

    您已将第一个参数映射到v,但您没有将它传递给真正的func。最好彻底删除。

    其次,也是最重要的:

    def pass_many(vals, some_funct, args=()):
        if not vals:
            return
        if isinstance(vals, Iterable) and not isinstance(vals, (basestring, Mapping)):
            for v in vals:
                pass_many(v, some_funct, args)
        else:
            some_funct(vals, *args)
    

    请注意,当您调用some_funct() 时,您正在解构args 参数。问题是,当你调用pass_many() 时,你给了它一个cursor 对象作为args 参数。这将巧妙地失败。要解决此问题,您可以删除 splat 运算符 *,或者,更好的选择是,您可以在调用 pass_many 时将 cursor 对象包装在一个元组中,如下所示:

    pass_many([["foo", "bar"], "pew"], insert, (cursor,))
    

    这仍然会报“no such table: wordlist”的错误,但这实际上是因为你没有定义这样的表。 ('wordlist' 是 insert() 的默认参数)

    【讨论】:

      猜你喜欢
      • 2021-11-21
      • 2014-11-17
      • 1970-01-01
      • 2018-08-24
      • 2014-10-01
      • 1970-01-01
      • 2018-05-30
      • 2022-07-12
      • 2020-05-16
      相关资源
      最近更新 更多