Python - 什么时候可以按名称传递位置参数,什么时候不能?

时间:2015-01-06 22:36:21

标签: python parameters arguments defaultdict

当您将default_factory作为位置参数传递时,Python 2.7.5 collections.defaultdict似乎才有效 - 当您将其作为命名参数传递时它会中断。

如果您运行以下代码,您会发现default_dict_success()运行良好,但default_dict_failure()会抛出KeyError

from collections import defaultdict

test_data = [
    ('clay', 'happy'),
    ('jason', 'happy'),
    ('aj', 'sad'),
    ('eric', 'happy'),
    ('sophie', 'sad')
]

def default_dict_success():
    results = defaultdict(list)
    for person, mood in test_data:
        results[mood].append(person)
    print results


def default_dict_failure():
    results = defaultdict(default_factory=list)
    for person, mood in test_data:
        results[mood].append(person)
    print results


default_dict_success()
default_dict_failure()

输出

# First function succeeds
defaultdict(<type 'list'>, {'sad': ['aj', 'sophie'], 'happy': ['clay', 'jason', 'eric']})

# Second function fails
Traceback (most recent call last):
  File "test_default_dict.py", line 26, in <module>
    default_dict_failure()
  File "test_default_dict.py", line 21, in default_dict_failure
    results[mood].append(person)
KeyError: 'happy'

任何人都知道发生了什么事?

编辑:最初我以为我正在查看一些Python源代码,这些源代码可能会提示我尝试做的事情是可能的,但评论者指出我错了,因为这个对象是在C中实现,因此没有Python源代码。所以它并不像我想象的那样相当

话说回来,这是我第一次遇到Python中的位置参数,也不能通过名字传递。这类事情会发生在其他地方吗?有没有办法在纯Python(而不是C扩展)中实现一个强制执行此类行为的函数?

2 个答案:

答案 0 :(得分:3)

在Modules / _collectionsmodule.c中,defdict_init()接受一个kwargs,但没有做任何事情,而不是传递给PyDict_Type.tp_init()。

IOW,defaultdict被记录为接受命名参数,但实现并不是,因此命名参数将被传递而不是被使用。

这可以使用PyArg_ParseTupleAndKeywords来修复,而不是将其参数视为一个简单的元组。 deque类型在同一模块中是一个如何完成它的例子,因为它接受了几个命名参数。

我猜测如果您在Python问题跟踪器中提交错误,则会更改文档以匹配实现,或者更改实施以匹配文档。

支持细节 - 当您使用default_factory命名参数创建defaultdict时,您会获得一个使用default_factory作为键预先创建的字典:

>>> import collections
>>> dd = collections.defaultdict(default_factory=int)
>>> dd
defaultdict(None, {'default_factory': <class 'int'>})
>>> dd2 = collections.defaultdict(int)
>>> dd2
defaultdict(<class 'int'>, {})
>>>

HTH

答案 1 :(得分:2)

我认为the docs试着说这会发生什么,虽然它们并不是特别清楚:

  

第一个参数提供default_factory属性的初始值;它默认为None。 所有剩余参数的处理方式与传递给dict构造函数的方式相同,包括关键字参数

强调我的。 “第一个参数”不是关键字参数(它们没有顺序)。也就是说,提交文档错误并不是一个坏主意。

  

话说回来,这是我第一次遇到Python中的位置参数,也不能通过名字传递。这类事情会发生在其他地方吗?有没有办法在纯Python(而不是C扩展)中实现一个强制执行此类行为的函数?

这实际上很常见,有一个whole PEP。将range视为一个简单示例。

关于自己这样做,

  

现代Python中实现的函数可以通过variadic * args参数接受任意数量的仅位置参数。但是,没有Python语法来指定接受特定数量的仅位置参数。换句话说,有许多内置函数,其签名根本无法用Python语法表达。

可以执行类似

的操作
def foo(*args):
    a, b, c = args

PEP中提到了这一点:

  

显然,人们可以通过接受(*args, **kwargs)并手动解析参数来模拟纯Python代码中的任何一个。但是这会导致Python函数的签名与它实际接受的签名之间脱节,更不用说实现所述参数解析的工作了。