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

时间:2022-01-11 16:37:48

The Python 2.7.5 collections.defaultdict only seems to work when you pass default_factory as a positional argument -- it breaks when you pass it as a named parameter.

当您将default_factory传递为一个位置参数时,defaultdict似乎只起作用——当您将它作为一个命名参数传递时,它就会中断。

If you run the following code you'll see that default_dict_success() runs fine, but default_dict_failure() throws a KeyError.

如果运行以下代码,您将看到default_dict_success()运行良好,但是default_dict_failure()抛出一个关键错误。

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()

The output is

输出是

# 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'

Anyone know what's going on?

有人知道发生了什么吗?

EDIT: Originally I thought I was looking at some Python source that would've suggested what I was trying to do was possible, but the commenters pointed out that I was mistaken, since this object is implemented in C and therefore there is no Python source for it. So it's not quite as mysterious as I thought.

编辑:最初我以为我在寻找一些Python源代码,这些源代码可以说明我正在尝试做的事情是可能的,但是评论者指出我错了,因为这个对象是用C实现的,因此它没有Python源代码。所以它并不像我想象的那么神秘。

That having been said, this is the first time I've come across positional argument in Python that couldn't also be passed by name. Does this type of thing happen anywhere else? Is there a way to implement a function in pure Python (as opposed to a C extension) that enforces this type of behavior?

话虽如此,这是我第一次在Python中遇到位置参数,它也不能通过名称传递。这种事情会在其他地方发生吗?有没有一种方法可以在纯Python(而不是C扩展)中实现执行这种行为的函数?

2 个解决方案

#1


2  

I think the docs try and say this is what will happen, although they aren't particularly clear:

我认为医生们会尝试说这就是将要发生的事情,尽管他们不是特别清楚:

The first argument provides the initial value for the default_factory attribute; it defaults to None. All remaining arguments are treated the same as if they were passed to the dict constructor, including keyword arguments.

第一个参数为default_factory属性提供初始值;没有默认值。所有剩余的参数都被当作传递给dict构造函数(包括关键字参数)的参数来处理。

Emphasis mine. The "first argument" wouldn't be a keyword argument (they have no order). That said, filing a documentation bug wouldn't be a bad idea.

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

That having been said, this is the first time I've come across positional argument in Python that couldn't also be passed by name. Does this type of thing happen anywhere else? Is there a way to implement a function in pure Python (as opposed to a C extension) that enforces this type of behavior?

话虽如此,这是我第一次在Python中遇到位置参数,它也不能通过名称传递。这种事情会在其他地方发生吗?有没有一种方法可以在纯Python(而不是C扩展)中实现执行这种行为的函数?

This is actually so common there's a whole PEP about it. Consider range as a simple example.

这是很常见的,有很多人都很兴奋。考虑范围作为一个简单的例子。

With regards to doing this yourself,

关于你自己做这件事,

Functions implemented in modern Python can accept an arbitrary number of positional-only arguments, via the variadic *args parameter. However, there is no Python syntax to specify accepting a specific number of positional-only parameters. Put another way, there are many builtin functions whose signatures are simply not expressable with Python syntax.

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

It is possible to do something like

有可能做类似的事情

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

This is mentioned in the PEP:

这在PEP中提到:

Obviously one can simulate any of these in pure Python code by accepting (*args, **kwargs) and parsing the arguments by hand. But this results in a disconnect between the Python function's signature and what it actually accepts, not to mention the work of implementing said argument parsing.

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

#2


3  

In Modules/_collectionsmodule.c, defdict_init() is accepting a kwargs, but not doing anything more with it than passing it through to PyDict_Type.tp_init().

在模块/ _collectionsmodule。c, defdict_init()接受一个kwargs,但除了将其传递给PyDict_Type.tp_init()之外,没有做任何其他事情。

IOW, defaultdict is documented as accepting a named argument, but the implementation doesn't, so the named argument gets passed through instead of being used.

IOW中,defaultdict词被记录为接受一个命名参数,但是实现没有,因此命名参数被传递,而不是被使用。

This could probably be fixed using PyArg_ParseTupleAndKeywords instead of treating its arguments as a simple tuple. The deque type, in the same module, is an example of how it could be done, as it accepts a couple of named arguments.

可以使用PyArg_ParseTupleAndKeywords来解决这个问题,而不是把它的参数当作一个简单的元组。在同一个模块中,deque类型是一个例子,它可以接受一些命名的参数。

I'm guessing that if you file a bug in the Python issue tracker, either the doc will be changed to match the implementation, or the implementation will be changed to match the doc.

我猜想,如果您在Python问题跟踪器中提交了一个bug,那么要么修改doc以匹配实现,要么修改实现以匹配doc。

Supporting detail - when you create a defaultdict with a default_factory named argument, you get a dictionary pre-created with default_factory as a key:

支持细节—当您创建一个带有名为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

HTH

#1


2  

I think the docs try and say this is what will happen, although they aren't particularly clear:

我认为医生们会尝试说这就是将要发生的事情,尽管他们不是特别清楚:

The first argument provides the initial value for the default_factory attribute; it defaults to None. All remaining arguments are treated the same as if they were passed to the dict constructor, including keyword arguments.

第一个参数为default_factory属性提供初始值;没有默认值。所有剩余的参数都被当作传递给dict构造函数(包括关键字参数)的参数来处理。

Emphasis mine. The "first argument" wouldn't be a keyword argument (they have no order). That said, filing a documentation bug wouldn't be a bad idea.

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

That having been said, this is the first time I've come across positional argument in Python that couldn't also be passed by name. Does this type of thing happen anywhere else? Is there a way to implement a function in pure Python (as opposed to a C extension) that enforces this type of behavior?

话虽如此,这是我第一次在Python中遇到位置参数,它也不能通过名称传递。这种事情会在其他地方发生吗?有没有一种方法可以在纯Python(而不是C扩展)中实现执行这种行为的函数?

This is actually so common there's a whole PEP about it. Consider range as a simple example.

这是很常见的,有很多人都很兴奋。考虑范围作为一个简单的例子。

With regards to doing this yourself,

关于你自己做这件事,

Functions implemented in modern Python can accept an arbitrary number of positional-only arguments, via the variadic *args parameter. However, there is no Python syntax to specify accepting a specific number of positional-only parameters. Put another way, there are many builtin functions whose signatures are simply not expressable with Python syntax.

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

It is possible to do something like

有可能做类似的事情

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

This is mentioned in the PEP:

这在PEP中提到:

Obviously one can simulate any of these in pure Python code by accepting (*args, **kwargs) and parsing the arguments by hand. But this results in a disconnect between the Python function's signature and what it actually accepts, not to mention the work of implementing said argument parsing.

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

#2


3  

In Modules/_collectionsmodule.c, defdict_init() is accepting a kwargs, but not doing anything more with it than passing it through to PyDict_Type.tp_init().

在模块/ _collectionsmodule。c, defdict_init()接受一个kwargs,但除了将其传递给PyDict_Type.tp_init()之外,没有做任何其他事情。

IOW, defaultdict is documented as accepting a named argument, but the implementation doesn't, so the named argument gets passed through instead of being used.

IOW中,defaultdict词被记录为接受一个命名参数,但是实现没有,因此命名参数被传递,而不是被使用。

This could probably be fixed using PyArg_ParseTupleAndKeywords instead of treating its arguments as a simple tuple. The deque type, in the same module, is an example of how it could be done, as it accepts a couple of named arguments.

可以使用PyArg_ParseTupleAndKeywords来解决这个问题,而不是把它的参数当作一个简单的元组。在同一个模块中,deque类型是一个例子,它可以接受一些命名的参数。

I'm guessing that if you file a bug in the Python issue tracker, either the doc will be changed to match the implementation, or the implementation will be changed to match the doc.

我猜想,如果您在Python问题跟踪器中提交了一个bug,那么要么修改doc以匹配实现,要么修改实现以匹配doc。

Supporting detail - when you create a defaultdict with a default_factory named argument, you get a dictionary pre-created with default_factory as a key:

支持细节—当您创建一个带有名为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

HTH