标签归档:parameter-passing

在Python中强制命名参数

问题:在Python中强制命名参数

在Python中,您可能有一个函数定义:

def info(object, spacing=10, collapse=1)

可以通过以下任何一种方式调用:

info(odbchelper)                    
info(odbchelper, 12)                
info(odbchelper, collapse=0)        
info(spacing=15, object=odbchelper)

多亏了Python允许任意顺序的参数(只要它们被命名)。

我们遇到的问题是,随着一些更大的函数的增长,人们可能会在spacing和之间添加参数collapse,这意味着错误的值可能会传递给未命名的参数。此外,有时不清楚需要输入什么。我们正在寻找一种方法来强迫人们命名某些参数-不仅是编码标准,还是理想的标志或pydev插件?

因此,在上述4个示例中,由于所有参数均已命名,因此只有最后一个示例可以通过检查。

奇怪的是,我们只会为某些功能打开它,但是有关如何实现此功能的任何建议-甚至可能的话,我们将不胜感激。

In Python you may have a function definition:

def info(object, spacing=10, collapse=1)

which could be called in any of the following ways:

info(odbchelper)                    
info(odbchelper, 12)                
info(odbchelper, collapse=0)        
info(spacing=15, object=odbchelper)

thanks to Python’s allowing of any-order arguments, so long as they’re named.

The problem we’re having is as some of our larger functions grow, people might be adding parameters between spacing and collapse, meaning that the wrong values may be going to parameters that aren’t named. In addition sometimes it’s not always clear as to what needs to go in. We’re after a way to force people to name certain parameters – not just a coding standard, but ideally a flag or pydev plugin?

so that in the above 4 examples, only the last would pass the check as all the parameters are named.

Odds are we’ll only turn it on for certain functions, but any suggestions as to how to implement this – or if it’s even possible would be appreciated.


回答 0

在Python 3中-是,您可以*在参数列表中指定。

文档

“ *”或“ * identifier”之后的参数仅是关键字参数,并且只能使用关键字参数传递。

>>> def foo(pos, *, forcenamed):
...   print(pos, forcenamed)
... 
>>> foo(pos=10, forcenamed=20)
10 20
>>> foo(10, forcenamed=20)
10 20
>>> foo(10, 20)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: foo() takes exactly 1 positional argument (2 given)

也可以结合使用**kwargs

def foo(pos, *, forcenamed, **kwargs):

In Python 3 – Yes, you can specify * in the argument list.

From docs:

Parameters after “*” or “*identifier” are keyword-only parameters and may only be passed used keyword arguments.

>>> def foo(pos, *, forcenamed):
...   print(pos, forcenamed)
... 
>>> foo(pos=10, forcenamed=20)
10 20
>>> foo(10, forcenamed=20)
10 20
>>> foo(10, 20)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: foo() takes exactly 1 positional argument (2 given)

This can also be combined with **kwargs:

def foo(pos, *, forcenamed, **kwargs):

回答 1

您可以通过以下方式定义函数来强制人们在Python3中使用关键字参数。

def foo(*, arg0="default0", arg1="default1", arg2="default2"):
    pass

通过将第一个参数设置为不带名称的位置参数,您可以强制每个调用该函数的人都使用关键字参数,这正是我想问的。在Python2中,唯一的方法是定义一个这样的函数

def foo(**kwargs):
    pass

这将迫使调用者使用kwargs,但这并不是一个很好的解决方案,因为您随后必须进行检查以仅接受所需的参数。

You can force people to use keyword arguments in Python3 by defining a function in the following way.

def foo(*, arg0="default0", arg1="default1", arg2="default2"):
    pass

By making the first argument a positional argument with no name you force everyone who calls the function to use the keyword arguments which is what I think you were asking about. In Python2 the only way to do this is to define a function like this

def foo(**kwargs):
    pass

That’ll force the caller to use kwargs but this isn’t that great of a solution as you’d then have to put a check to only accept the argument that you need.


回答 2

的确,大多数编程语言都将参数顺序作为函数调用协定的一部分,但这不是必须的。为什么会这样?我对这个问题的理解是,Python在这方面是否与其他编程语言有所不同。除了适用于Python 2的其他良好答案外,请考虑以下因素:

__named_only_start = object()

def info(param1,param2,param3,_p=__named_only_start,spacing=10,collapse=1):
    if _p is not __named_only_start:
        raise TypeError("info() takes at most 3 positional arguments")
    return str(param1+param2+param3) +"-"+ str(spacing) +"-"+ str(collapse)

调用方能够提供参数spacing并按collapse位置(无exceptions)提供的唯一方法是:

info(arg1, arg2, arg3, module.__named_only_start, 11, 2)

在Python中,不使用属于其他模块的私有元素的约定已经非常基本。与Python本身一样,这种参数约定只能被强制执行。

否则,调用将采用以下形式:

info(arg1, arg2, arg3, spacing=11, collapse=2)

一个电话

info(arg1, arg2, arg3, 11, 2)

将为参数分配值11 _p以及该函数的第一条指令引发的异常。

特点:

  • 之前_p=__named_only_start的参数按位置(或按名称)被接受。
  • 之后的参数_p=__named_only_start必须仅通过名称提供(除非__named_only_start获得并使用了有关特殊前哨对象的知识)。

优点:

  • 参数在数量和含义上都是明确的(当然,如果还选择了好名字,则在后面)。
  • 如果将前哨指定为第一个参数,则所有参数都需要按名称指定。
  • 调用该函数时,可以通过__named_only_start在相应位置使用哨兵对象来切换到位置模式。
  • 可以预见到比其他替代方案更好的性能。

缺点:

  • 检查发生在运行时,而不是编译时。
  • 使用额外的参数(尽管不是参数)和额外的检查。相对于常规功能而言,性能下降较小。
  • 功能是没有该语言直接支持的黑客(请参阅下面的注释)。
  • 调用该函数时,可以通过__named_only_start在正确的位置使用哨兵对象来切换到位置模式。是的,这也可以看作是专业人士。

请记住,该答案仅对Python 2有效。Python3实现了类似的,但非常优雅的,语言支持的机制,在其他答案中也有描述。

我发现,当我打开思路思考时,没有问题或其他人的决定看起来是愚蠢,愚蠢或愚蠢的。恰恰相反:我通常会学到很多东西。

True, most programming languages make parameter order part of the function call contract, but this doesn’t need to be so. Why would it? My understanding of the question is, then, if Python is any different to other programming languages in this respect. In addition to other good answers for Python 2, please consider the following:

__named_only_start = object()

def info(param1,param2,param3,_p=__named_only_start,spacing=10,collapse=1):
    if _p is not __named_only_start:
        raise TypeError("info() takes at most 3 positional arguments")
    return str(param1+param2+param3) +"-"+ str(spacing) +"-"+ str(collapse)

The only way a caller would be able to provide arguments spacing and collapse positionally (without an exception) would be:

info(arg1, arg2, arg3, module.__named_only_start, 11, 2)

The convention of not using private elements belonging to other modules already is very basic in Python. As with Python itself, this convention for parameters would only be semi-enforced.

Otherwise, calls would need to be of the form:

info(arg1, arg2, arg3, spacing=11, collapse=2)

A call

info(arg1, arg2, arg3, 11, 2)

would assign value 11 to parameter _p and an exception risen by the function’s first instruction.

Characteristics:

  • Parameters before _p=__named_only_start are admitted positionally (or by name).
  • Parameters after _p=__named_only_start must be provided by name only (unless knowledge about the special sentinel object __named_only_start is obtained and used).

Pros:

  • Parameters are explicit in number and meaning (the later if good names are also chosen, of course).
  • If the sentinel is specified as first parameter, then all arguments need to be specified by name.
  • When calling the function, it’s possible to switch to positional mode by using the sentinel object __named_only_start in the corresponding position.
  • A better performance than other alternatives can be anticipated.

Cons:

  • Checking occurs during run-time, not compile-time.
  • Use of an extra parameter (though not argument) and an additional check. Small performance degradation respect to regular functions.
  • Functionality is a hack without direct support by the language (see note below).
  • When calling the function, it’s possible to switch to positional mode by using the sentinel object __named_only_start in the right position. Yes, this can also be seen as a pro.

Please do keep in mind that this answer is only valid for Python 2. Python 3 implements the similar, but very elegant, language-supported mechanism described in other answers.

I’ve found that when I open my mind and think about it, no question or other’s decision seems stupid, dumb, or just silly. Quite on the contrary: I typically learn a lot.


回答 3

您可以通过使“伪造的”第一个关键字参数具有默认值而不会“自然地”出现,从而以在Python 2和Python 3中都可以使用的方式 来实现。该关键字参数前面可以有一个或多个没有值的参数:

_dummy = object()

def info(object, _kw=_dummy, spacing=10, collapse=1):
    if _kw is not _dummy:
        raise TypeError("info() takes 1 positional argument but at least 2 were given")

这将允许:

info(odbchelper)        
info(odbchelper, collapse=0)        
info(spacing=15, object=odbchelper)

但不是:

info(odbchelper, 12)                

如果将功能更改为:

def info(_kw=_dummy, spacing=10, collapse=1):

那么所有参数都必须具有关键字,并且info(odbchelper)将不再起作用。

这样,您便可以将其他关键字参数放在后面的任何位置_kw,而不必强迫您将其放在最后一个条目之后。这通常是有道理的,例如,按逻辑对事物进行分组或按字母顺序排列关键字可以帮助维护和开发。

因此,无需还原到def(**kwargs)在智能编辑器中使用和丢失签名信息。您的社会契约是通过强迫(其中一些)要求关键字(它们的显示顺序)变得不相关来提供某些信息。

You can do that in a way that works in both Python 2 and Python 3, by making a “bogus” first keyword argument with a default value that will not occur “naturally”. That keyword argument can be preceded by one or more arguments without value:

_dummy = object()

def info(object, _kw=_dummy, spacing=10, collapse=1):
    if _kw is not _dummy:
        raise TypeError("info() takes 1 positional argument but at least 2 were given")

This will allow:

info(odbchelper)        
info(odbchelper, collapse=0)        
info(spacing=15, object=odbchelper)

but not:

info(odbchelper, 12)                

If you change the function to:

def info(_kw=_dummy, spacing=10, collapse=1):

then all arguments must have keywords and info(odbchelper) will no longer work.

This will allow you to position additional keyword arguments any place after _kw, without forcing you to put them after the last entry. This often makes sense, e.g. grouping thing logically or arranging keywords alphabetically can help with maintenance and development.

So there is no need to revert to using def(**kwargs) and losing the signature information in your smart editor. Your social contract is to provide certain information, by forcing (some of them) to require keywords, the order these are presented in, has become irrelevant.


回答 4

更新:

我意识到使用**kwargs并不能解决问题。如果您的程序员根据需要更改函数参数,则可以例如将函数更改为:

def info(foo, **kwargs):

并且旧代码将再次中断(因为现在每个函数调用都必须包含第一个参数)。

确实归结为布莱恩所说的话。


(…)人们可能在spacingcollapse(…)之间添加了参数

通常,在更改函数时,新参数应始终结尾。否则,它将破坏代码。应该很明显。
如果有人更改了功能使代码中断,则必须拒绝此更改。
(正如布莱恩所说,这就像是一份合同)

(…)有时不清楚需要输入什么。

通过查看函数的签名(即def info(object, spacing=10, collapse=1)),应该立即看到每个没有默认值的参数都是强制性的。参数的用途是
什么,应该放在文档字符串中。


旧答案(保持完整性)

这可能不是一个好的解决方案:

您可以通过以下方式定义函数:

def info(**kwargs):
    ''' Some docstring here describing possible and mandatory arguments. '''
    spacing = kwargs.get('spacing', 15)
    obj = kwargs.get('object', None)
    if not obj:
       raise ValueError('object is needed')

kwargs是包含任何关键字参数的字典。您可以检查是否存在强制性参数,如果不存在,则引发异常。

不利的一面是,可能不再是显而易见的,哪些参数是可能的,但是使用适当的文档字符串,应该没问题。

Update:

I realized that using **kwargs would not solve the problem. If your programmers change function arguments as they wish, one could, for example, change the function to this:

def info(foo, **kwargs):

and the old code would break again (because now every function call has to include the first argument).

It really comes down to what Bryan says.


(…) people might be adding parameters between spacing and collapse (…)

In general, when changing functions, new arguments should always go to the end. Otherwise it breaks the code. Should be obvious.
If someone changes the function so that the code breaks, this change has to be rejected.
(As Bryan says, it is like a contract)

(…) sometimes it’s not always clear as to what needs to go in.

By looking at the signature of the function (i.e def info(object, spacing=10, collapse=1) ) one should immediately see that every argument that has not a default value, is mandatory.
What the argument is for, should go into the docstring.


Old answer (kept for completeness):

This is probably not a good solution:

You can define functions this way:

def info(**kwargs):
    ''' Some docstring here describing possible and mandatory arguments. '''
    spacing = kwargs.get('spacing', 15)
    obj = kwargs.get('object', None)
    if not obj:
       raise ValueError('object is needed')

kwargs is a dictionary that contains any keyword argument. You can check whether a mandatory argument is present and if not, raise an exception.

The downside is, that it might not be that obvious anymore, which arguments are possible, but with a proper docstring, it should be fine.


回答 5

python3-only关键字参数(*)可以在python2.x中使用**kwargs

考虑以下python3代码:

def f(pos_arg, *, no_default, has_default='default'):
    print(pos_arg, no_default, has_default)

及其行为:

>>> f(1, 2, 3)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: f() takes 1 positional argument but 3 were given
>>> f(1, no_default='hi')
1 hi default
>>> f(1, no_default='hi', has_default='hello')
1 hi hello
>>> f(1)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: f() missing 1 required keyword-only argument: 'no_default'
>>> f(1, no_default=1, wat='wat')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: f() got an unexpected keyword argument 'wat'

可以使用以下方法对此进行模拟,请注意TypeErrorKeyError在“必需的命名参数”情况下,我可以自由切换到,同样要使相同的异常类型也不会花费太多工作

def f(pos_arg, **kwargs):
    no_default = kwargs.pop('no_default')
    has_default = kwargs.pop('has_default', 'default')
    if kwargs:
        raise TypeError('unexpected keyword argument(s) {}'.format(', '.join(sorted(kwargs))))

    print(pos_arg, no_default, has_default)

行为:

>>> f(1, 2, 3)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: f() takes exactly 1 argument (3 given)
>>> f(1, no_default='hi')
(1, 'hi', 'default')
>>> f(1, no_default='hi', has_default='hello')
(1, 'hi', 'hello')
>>> f(1)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 2, in f
KeyError: 'no_default'
>>> f(1, no_default=1, wat='wat')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 6, in f
TypeError: unexpected keyword argument(s) wat

该食谱在python3.x中同样有效,但是如果您仅在python3.x中应避免使用

The python3 keyword-only arguments (*) can be simulated in python2.x with **kwargs

Consider the following python3 code:

def f(pos_arg, *, no_default, has_default='default'):
    print(pos_arg, no_default, has_default)

and its behaviour:

>>> f(1, 2, 3)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: f() takes 1 positional argument but 3 were given
>>> f(1, no_default='hi')
1 hi default
>>> f(1, no_default='hi', has_default='hello')
1 hi hello
>>> f(1)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: f() missing 1 required keyword-only argument: 'no_default'
>>> f(1, no_default=1, wat='wat')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: f() got an unexpected keyword argument 'wat'

This can be simulated using the following, note I’ve taken the liberty of switching TypeError to KeyError in the “required named argument” case, it wouldn’t be too much work to make that the same exception type as well

def f(pos_arg, **kwargs):
    no_default = kwargs.pop('no_default')
    has_default = kwargs.pop('has_default', 'default')
    if kwargs:
        raise TypeError('unexpected keyword argument(s) {}'.format(', '.join(sorted(kwargs))))

    print(pos_arg, no_default, has_default)

And behaviour:

>>> f(1, 2, 3)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: f() takes exactly 1 argument (3 given)
>>> f(1, no_default='hi')
(1, 'hi', 'default')
>>> f(1, no_default='hi', has_default='hello')
(1, 'hi', 'hello')
>>> f(1)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 2, in f
KeyError: 'no_default'
>>> f(1, no_default=1, wat='wat')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 6, in f
TypeError: unexpected keyword argument(s) wat

The recipe works equally as well in python3.x, but should be avoided if you are python3.x only


回答 6

您可以将函数声明为**args仅接收。这将强制使用关键字参数,但是您需要做一些额外的工作以确保仅传递有效名称。

def foo(**args):
   print args

foo(1,2) # Raises TypeError: foo() takes exactly 0 arguments (2 given)
foo(hello = 1, goodbye = 2) # Works fine.

You could declare your functions as receiving **args only. That would mandate keyword arguments but you’d have some extra work to make sure only valid names are passed in.

def foo(**args):
   print args

foo(1,2) # Raises TypeError: foo() takes exactly 0 arguments (2 given)
foo(hello = 1, goodbye = 2) # Works fine.

回答 7

正如其他答案所说,更改功能签名是一个坏主意。在末尾添加新参数,或者在插入参数的情况下修复每个调用方。

如果仍要执行此操作,请使用函数装饰器inspect.getargspec函数。它将使用如下形式:

@require_named_args
def info(object, spacing=10, collapse=1):
    ....

的实现require_named_args留给读者练习。

我不会打扰。每次调用该函数的速度都会很慢,通过更仔细地编写代码,您将获得更好的结果。

As other answers say, changing function signatures is a bad idea. Either add new parameters to the end, or fix every caller if arguments are inserted.

If you still want to do it, use a function decorator and the inspect.getargspec function. It would be used something like this:

@require_named_args
def info(object, spacing=10, collapse=1):
    ....

Implementation of require_named_args is left as an exercise for the reader.

I would not bother. It will be slow every time the function is called, and you will get better results from writing code more carefully.


回答 8

您可以使用**运算符:

def info(**kwargs):

这样,人们被迫使用命名参数。

You could use the ** operator:

def info(**kwargs):

this way people are forced to use named parameters.


回答 9

def cheeseshop(kind, *arguments, **keywords):

在python中,如果使用* args,则意味着您可以为该参数传递n个参数-这将成为函数内部的列表以访问

如果使用** kw表示其关键字参数,则可以按dict的方式进行访问-您可以传递n个数量的kw args,并且如果要限制该用户必须按顺序输入序列和参数,则不要使用*和**-(它为大型架构提供通用解决方案的pythonic方法…)

如果要使用默认值限制功能,则可以在其中检查

def info(object, spacing, collapse)
  spacing = spacing or 10
  collapse = collapse or 1
def cheeseshop(kind, *arguments, **keywords):

in python if use *args that means you can pass n-number of positional arguments for this parameter – which will be accessed as a tuple inside the function.

And if use **kw that means its keyword arguments, that can be access as dict – you can pass n-number of kw args, and if you want to restrict that user must enter the sequence and arguments in order then don’t use * and ** – (its pythonic way to provide generic solutions for big architectures…)

if you want to restrict your function with default values then you can check inside it

def info(object, spacing, collapse)
  spacing = 10 if spacing is None else spacing
  collapse = 1 if collapse is None else collapse

回答 10

我不明白为什么程序员会首先在其他两个之间添加参数。

如果您希望函数参数与名称一起使用(例如, info(spacing=15, object=odbchelper)),则定义它们的顺序无关紧要,因此您最好将新参数放在最后。

如果您确实希望订单很重要,那么更改后就别指望了!

I don’t get why a programmer will add a parameter in between two others in the first place.

If you want the function parameters to be used with names (e.g. info(spacing=15, object=odbchelper) ) then it shouldn’t matter what order they are defined in, so you might as well put the new parameters at the end.

If you do want the order to matter then can’t expect anything to work if you change it!


如何在Python中将方法作为参数传递

问题:如何在Python中将方法作为参数传递

是否可以将方法作为参数传递给方法?

self.method2(self.method1)

def method1(self):
    return 'hello world'

def method2(self, methodToRun):
    result = methodToRun.call()
    return result

Is it possible to pass a method as a parameter to a method?

self.method2(self.method1)

def method1(self):
    return 'hello world'

def method2(self, methodToRun):
    result = methodToRun.call()
    return result

回答 0

是的,只需使用您编写的方法名称即可。方法/函数是Python中的对象,就像其他任何对象一样,您可以在执行变量的过程中传递它们。实际上,您可以将方法(或函数)视为变量,其值是实际的可调用代码对象。

仅供参考,没有call方法-我认为它叫做__call__,但是您不必显式调用它:

def method1():
    return 'hello world'

def method2(methodToRun):
    result = methodToRun()
    return result

method2(method1)

如果您想method1使用参数来调用,那么事情会变得有些复杂。method2必须写一些有关如何将参数传递给的信息method1,并且它需要从某个地方获取这些参数的值。例如,if method1应该采用一个参数:

def method1(spam):
    return 'hello ' + str(spam)

那么您可以编写method2一个传入的参数来调用它:

def method2(methodToRun, spam_value):
    return methodToRun(spam_value)

或带有它自己计算的参数:

def method2(methodToRun):
    spam_value = compute_some_value()
    return methodToRun(spam_value)

您可以将其扩展为传入的值和计算的值的其他组合,例如

def method1(spam, ham):
    return 'hello ' + str(spam) + ' and ' + str(ham)

def method2(methodToRun, ham_value):
    spam_value = compute_some_value()
    return methodToRun(spam_value, ham_value)

甚至带有关键字参数

def method2(methodToRun, ham_value):
    spam_value = compute_some_value()
    return methodToRun(spam_value, ham=ham_value)

如果您不知道在编写时要使用method2什么参数methodToRun,也可以使用参数拆包以通用方式调用它:

def method1(spam, ham):
    return 'hello ' + str(spam) + ' and ' + str(ham)

def method2(methodToRun, positional_arguments, keyword_arguments):
    return methodToRun(*positional_arguments, **keyword_arguments)

method2(method1, ['spam'], {'ham': 'ham'})

在这种情况下positional_arguments,必须是列表或元组或类似内容,并且keyword_arguments是字典或类似内容。在调用之前,您method2可以修改positional_argumentskeyword_arguments(例如,添加或删除某些参数或更改值)method1

Yes it is, just use the name of the method, as you have written. Methods and functions are objects in Python, just like anything else, and you can pass them around the way you do variables. In fact, you can think about a method (or function) as a variable whose value is the actual callable code object.

Since you asked about methods, I’m using methods in the following examples, but note that everything below applies identically to functions (except without the self parameter).

To call a passed method or function, you just use the name it’s bound to in the same way you would use the method’s (or function’s) regular name:

def method1(self):
    return 'hello world'

def method2(self, methodToRun):
    result = methodToRun()
    return result

obj.method2(obj.method1)

Note: I believe a __call__() method does exist, i.e. you could technically do methodToRun.__call__(), but you probably should never do so explicitly. __call__() is meant to be implemented, not to be invoked from your own code.

If you wanted method1 to be called with arguments, then things get a little bit more complicated. method2 has to be written with a bit of information about how to pass arguments to method1, and it needs to get values for those arguments from somewhere. For instance, if method1 is supposed to take one argument:

def method1(self, spam):
    return 'hello ' + str(spam)

then you could write method2 to call it with one argument that gets passed in:

def method2(self, methodToRun, spam_value):
    return methodToRun(spam_value)

or with an argument that it computes itself:

def method2(self, methodToRun):
    spam_value = compute_some_value()
    return methodToRun(spam_value)

You can expand this to other combinations of values passed in and values computed, like

def method1(self, spam, ham):
    return 'hello ' + str(spam) + ' and ' + str(ham)

def method2(self, methodToRun, ham_value):
    spam_value = compute_some_value()
    return methodToRun(spam_value, ham_value)

or even with keyword arguments

def method2(self, methodToRun, ham_value):
    spam_value = compute_some_value()
    return methodToRun(spam_value, ham=ham_value)

If you don’t know, when writing method2, what arguments methodToRun is going to take, you can also use argument unpacking to call it in a generic way:

def method1(self, spam, ham):
    return 'hello ' + str(spam) + ' and ' + str(ham)

def method2(self, methodToRun, positional_arguments, keyword_arguments):
    return methodToRun(*positional_arguments, **keyword_arguments)

obj.method2(obj.method1, ['spam'], {'ham': 'ham'})

In this case positional_arguments needs to be a list or tuple or similar, and keyword_arguments is a dict or similar. In method2 you can modify positional_arguments and keyword_arguments (e.g. to add or remove certain arguments or change the values) before you call method1.


回答 1

是的,有可能。只是称呼它:

class Foo(object):
    def method1(self):
        pass
    def method2(self, method):
        return method()

foo = Foo()
foo.method2(foo.method1)

Yes it is possible. Just call it:

class Foo(object):
    def method1(self):
        pass
    def method2(self, method):
        return method()

foo = Foo()
foo.method2(foo.method1)

回答 2

这是重写您的示例以显示一个独立的工作示例:

class Test:
    def method1(self):
        return 'hello world'

    def method2(self, methodToRun):
        result = methodToRun()
        return result

    def method3(self):
        return self.method2(self.method1)

test = Test()

print test.method3()

Here is your example re-written to show a stand-alone working example:

class Test:
    def method1(self):
        return 'hello world'

    def method2(self, methodToRun):
        result = methodToRun()
        return result

    def method3(self):
        return self.method2(self.method1)

test = Test()

print test.method3()

回答 3

是; 函数(和方法)是Python中的一流对象。以下作品:

def foo(f):
    print "Running parameter f()."
    f()

def bar():
    print "In bar()."

foo(bar)

输出:

Running parameter f().
In bar().

使用Python解释器或IPython shell (对于更多功能)可以轻松回答这些问题。

Yes; functions (and methods) are first class objects in Python. The following works:

def foo(f):
    print "Running parameter f()."
    f()

def bar():
    print "In bar()."

foo(bar)

Outputs:

Running parameter f().
In bar().

These sorts of questions are trivial to answer using the Python interpreter or, for more features, the IPython shell.


回答 4

如果您想将类的方法作为参数传递,但是还没有要调用的对象,则只需将对象作为第一个参数(即“自身”)传递即可论据)。

class FooBar:

    def __init__(self, prefix):
        self.prefix = prefix

    def foo(self, name):
        print "%s %s" % (self.prefix, name)


def bar(some_method):
    foobar = FooBar("Hello")
    some_method(foobar, "World")

bar(FooBar.foo)

这将打印“ Hello World”

If you want to pass a method of a class as an argument but don’t yet have the object on which you are going to call it, you can simply pass the object once you have it as the first argument (i.e. the “self” argument).

class FooBar:

    def __init__(self, prefix):
        self.prefix = prefix

    def foo(self, name):
        print "%s %s" % (self.prefix, name)


def bar(some_method):
    foobar = FooBar("Hello")
    some_method(foobar, "World")

bar(FooBar.foo)

This will print “Hello World”


回答 5

很多好的答案,但奇怪的是没有人提到使用lambda函数。
因此,如果没有参数,事情就会变得很简单:

def method1():
    return 'hello world'

def method2(methodToRun):
    result = methodToRun()
    return result

method2(method1)

但是请说您在一个(或多个)参数中method1

def method1(param):
    return 'hello ' + str(param)

def method2(methodToRun):
    result = methodToRun()
    return result

然后,您可以简单地调用method2as method2(lambda: method1('world'))

method2(lambda: method1('world'))
>>> hello world
method2(lambda: method1('reader'))
>>> hello reader

我发现这里的答案比这里提到的其他答案要干净得多。

Lots of good answers but strange that no one has mentioned using a lambda function.
So if you have no arguments, things become pretty trivial:

def method1():
    return 'hello world'

def method2(methodToRun):
    result = methodToRun()
    return result

method2(method1)

But say you have one (or more) arguments in method1:

def method1(param):
    return 'hello ' + str(param)

def method2(methodToRun):
    result = methodToRun()
    return result

Then you can simply invoke method2 as method2(lambda: method1('world')).

method2(lambda: method1('world'))
>>> hello world
method2(lambda: method1('reader'))
>>> hello reader

I find this much cleaner than the other answers mentioned here.


回答 6

方法是任何其他对象。因此,您可以将它们传递出去,将它们存储在列表和字典中,并随心所欲地对其进行操作。关于它们的特殊之处在于它们是可调用对象,因此您可以__call__在它们上调用。__call__当您调用带有或不带有参数的方法时,会被自动调用,因此您只需要编写即可methodToRun()

Methods are objects like any other. So you can pass them around, store them in lists and dicts, do whatever you like with them. The special thing about them is they are callable objects so you can invoke __call__ on them. __call__ gets called automatically when you invoke the method with or without arguments so you just need to write methodToRun().


回答 7

并非完全符合您的需要,但一个相关的有用工具是getattr()使用方法名称作为参数。

class MyClass:
   def __init__(self):
      pass
   def MyMethod(self):
      print("Method ran")

# Create an object
object = MyClass()
# Get all the methods of a class
method_list = [func for func in dir(MyClass) if callable(getattr(MyClass, func))]
# You can use any of the methods in method_list
# "MyMethod" is the one we want to use right now

# This is the same as running "object.MyMethod()"
getattr(object,'MyMethod')()

Not exactly what you want, but a related useful tool is getattr(), to use method’s name as a parameter.

class MyClass:
   def __init__(self):
      pass
   def MyMethod(self):
      print("Method ran")

# Create an object
object = MyClass()
# Get all the methods of a class
method_list = [func for func in dir(MyClass) if callable(getattr(MyClass, func))]
# You can use any of the methods in method_list
# "MyMethod" is the one we want to use right now

# This is the same as running "object.MyMethod()"
getattr(object,'MyMethod')()

函数参数中仅带有星号?

问题:函数参数中仅带有星号?

函数参数中的星号有什么作用?

当我查看pickle模块时,我看到了:

pickle.dump(obj, file, protocol=None, *, fix_imports=True)

我知道在参数之前(对于可变数量的参数)之前有一个单星号和一个双星号,但是这没有任何前缀。而且我敢肯定,这与泡菜无关。那可能只是这种情况的一个例子。我将其发送给翻译人员时才知道它的名字:

>>> def func(*):
...     pass
...
  File "<stdin>", line 1
SyntaxError: named arguments must follow bare *

如果重要的话,我使用的是python 3.3.0。

What does a bare asterisk in the arguments of a function do?

When I looked at the pickle module, I see this:

pickle.dump(obj, file, protocol=None, *, fix_imports=True)

I know about a single and double asterisks preceding arguments (for variable number of arguments), but this precedes nothing. And I’m pretty sure this has nothing to do with pickle. That’s probably just an example of this happening. I only learned its name when I sent this to the interpreter:

>>> def func(*):
...     pass
...
  File "<stdin>", line 1
SyntaxError: named arguments must follow bare *

If it matters, I’m on python 3.3.0.


回答 0

Bare *用于强制调用者使用命名参数-因此,如果*没有以下关键字参数,则无法将函数定义为参数。

有关更多详细信息,请参见此答案Python 3文档

Bare * is used to force the caller to use named arguments – so you cannot define a function with * as an argument when you have no following keyword arguments.

See this answer or Python 3 documentation for more details.


回答 1

当原始答案完全回答问题时,只需添加一些相关信息即可。单个星号的行为源自PEP-3102。引用相关部分:

The second syntactical change is to allow the argument name to
be omitted for a varargs argument. The meaning of this is to
allow for keyword-only arguments for functions that would not
otherwise take a varargs argument:

    def compare(a, b, *, key=None):
        ...

简单来说,这意味着要传递key的值,您需要将其显式传递为key="value"

While the original answer answers the question completely, just adding a bit of related information. The behaviour for the single asterisk derives from PEP-3102. Quoting the related section:

The second syntactical change is to allow the argument name to
be omitted for a varargs argument. The meaning of this is to
allow for keyword-only arguments for functions that would not
otherwise take a varargs argument:

    def compare(a, b, *, key=None):
        ...

In simple english, it means that to pass the value for key, you will need to explicitly pass it as key="value".


回答 2

def func(*, a, b):
    print(a)
    print(b)

func("gg") # TypeError: func() takes 0 positional arguments but 1 was given
func(a="gg") # TypeError: func() missing 1 required keyword-only argument: 'b'
func(a="aa", b="bb", c="cc") # TypeError: func() got an unexpected keyword argument 'c'
func(a="aa", b="bb", "cc") # SyntaxError: positional argument follows keyword argument
func(a="aa", b="bb") # aa, bb

上面带有** kwargs的示例

def func(*, a, b, **kwargs):
    print(a)
    print(b)
    print(kwargs)

func(a="aa",b="bb", c="cc") # aa, bb, {'c': 'cc'}
def func(*, a, b):
    print(a)
    print(b)

func("gg") # TypeError: func() takes 0 positional arguments but 1 was given
func(a="gg") # TypeError: func() missing 1 required keyword-only argument: 'b'
func(a="aa", b="bb", c="cc") # TypeError: func() got an unexpected keyword argument 'c'
func(a="aa", b="bb", "cc") # SyntaxError: positional argument follows keyword argument
func(a="aa", b="bb") # aa, bb

the above example with **kwargs

def func(*, a, b, **kwargs):
    print(a)
    print(b)
    print(kwargs)

func(a="aa",b="bb", c="cc") # aa, bb, {'c': 'cc'}

回答 3

从语义上讲,这意味着其后的参数仅是关键字,因此,如果尝试在不指定名称的情况下提供参数,则会出现错误。例如:

>>> def f(a, *, b):
...     return a + b
...
>>> f(1, 2)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: f() takes 1 positional argument but 2 were given
>>> f(1, b=2)
3

在实用上,这意味着您必须使用关键字参数来调用该函数。如果在没有参数名称给出提示的情况下很难理解参数的目的,通常会这样做。

比较例如sorted(nums, reverse=True)vs.如果您写过sorted(nums, True)。后者的可读性要差得多,因此Python开发人员选择让您以前一种方式编写它。

Semantically, it means the arguments following it are keyword-only, so you will get an error if you try to provide an argument without specifying its name. For example:

>>> def f(a, *, b):
...     return a + b
...
>>> f(1, 2)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: f() takes 1 positional argument but 2 were given
>>> f(1, b=2)
3

Pragmatically, it means you have to call the function with a keyword argument. It’s usually done when it would be hard to understand the purpose of the argument without the hint given by the argument’s name.

Compare e.g. sorted(nums, reverse=True) vs. if you wrote sorted(nums, True). The latter would be much less readable, so the Python developers chose to make you to write it the former way.


回答 4

假设您具有以下功能:

def sum(a,key=5):
    return a + key 

您可以通过两种方式调用此函数:

sum(1,2) 要么 sum(1,key=2)

假设您只想sum使用关键字参数来调用函数。

您添加*到函数参数列表以标记位置参数的结尾。

所以函数定义为:

def sum(a,*,key=5):
    return a + key 

只能使用 sum(1,key=2)

Suppose you have function:

def sum(a,key=5):
    return a + key 

You can call this function in 2 ways:

sum(1,2) or sum(1,key=2)

Suppose you want function sum to be called only using keyword arguments.

You add * to the function parameter list to mark the end of positional arguments.

So function defined as:

def sum(a,*,key=5):
    return a + key 

may be called only using sum(1,key=2)


回答 5

我发现下面的链接,是非常有益的解释**args以及**kwargs

https://pythontips.com/2013/08/04/args-and-kwargs-in-python-explained/

本质上,除了上面的答案之外,我还从上面的网站(信用:https : //pythontips.com/author/yasoob008/)学到了以下内容:

在下面首先定义演示功能的情况下,有两个示例,一个带有*args和一个带有**kwargs

def test_args_kwargs(arg1, arg2, arg3):
    print "arg1:", arg1
    print "arg2:", arg2
    print "arg3:", arg3

# first with *args
>>> args = ("two", 3,5)
>>> test_args_kwargs(*args)
arg1: two
arg2: 3
arg3: 5

# now with **kwargs:
>>> kwargs = {"arg3": 3, "arg2": "two","arg1":5}
>>> test_args_kwargs(**kwargs)
arg1: 5
arg2: two
arg3: 3

因此*args,您可以动态建立一个参数列表,该参数列表将按照其输入的顺序进行处理,而**kwargs可以启用NAMED参数的传递,并且可以由NAME进行相应的处理(而与它们的输入顺序无关) 。

该站点继续,并指出参数的正确顺序应为:

some_func(fargs,*args,**kwargs)

I’ve found the following link to be very helpful explaining *, *args and **kwargs:

https://pythontips.com/2013/08/04/args-and-kwargs-in-python-explained/

Essentially, in addition to the answers above, I’ve learned from the site above (credit: https://pythontips.com/author/yasoob008/) the following:

With the demonstration function defined first below, there are two examples, one with *args and one with **kwargs

def test_args_kwargs(arg1, arg2, arg3):
    print "arg1:", arg1
    print "arg2:", arg2
    print "arg3:", arg3

# first with *args
>>> args = ("two", 3,5)
>>> test_args_kwargs(*args)
arg1: two
arg2: 3
arg3: 5

# now with **kwargs:
>>> kwargs = {"arg3": 3, "arg2": "two","arg1":5}
>>> test_args_kwargs(**kwargs)
arg1: 5
arg2: two
arg3: 3

So *args allows you to dynamically build a list of arguments that will be taken in the order in which they are fed, whereas **kwargs can enable the passing of NAMED arguments, and can be processed by NAME accordingly (irrespective of the order in which they are fed).

The site continues, noting that the correct ordering of arguments should be:

some_func(fargs,*args,**kwargs)

在函数调用中,星号运算符是什么意思?

问题:在函数调用中,星号运算符是什么意思?

*运算符在Python中是什么意思,例如likezip(*x)或代码f(**k)

  1. 在解释器内部如何处理?
  2. 它会影响性能吗?是快还是慢?
  3. 什么时候有用,什么时候没有?
  4. 应该在函数声明中还是在调用中使用它?

What does the * operator mean in Python, such as in code like zip(*x) or f(**k)?

  1. How is it handled internally in the interpreter?
  2. Does it affect performance at all? Is it fast or slow?
  3. When is it useful and when is it not?
  4. Should it be used in a function declaration or in a call?

回答 0

单颗星*将序列/集合解压缩为位置参数,因此您可以执行以下操作:

def sum(a, b):
    return a + b

values = (1, 2)

s = sum(*values)

这将打开元组的包装,使其实际执行为:

s = sum(1, 2)

双星**只使用字典并因此命名参数来做同样的事情:

values = { 'a': 1, 'b': 2 }
s = sum(**values)

您还可以结合:

def sum(a, b, c, d):
    return a + b + c + d

values1 = (1, 2)
values2 = { 'c': 10, 'd': 15 }
s = sum(*values1, **values2)

将执行为:

s = sum(1, 2, c=10, d=15)

另请参见Python文档的4.7.4-解包参数列表


另外,您可以定义要接受的函数*x**y参数,这使函数可以接受在声明中未专门命名的任何数量的位置和/或命名参数。

例:

def sum(*values):
    s = 0
    for v in values:
        s = s + v
    return s

s = sum(1, 2, 3, 4, 5)

或搭配**

def get_a(**values):
    return values['a']

s = get_a(a=1, b=2)      # returns 1

这可以使您无需声明它们即可指定大量可选参数。

再一次,您可以结合:

def sum(*values, **options):
    s = 0
    for i in values:
        s = s + i
    if "neg" in options:
        if options["neg"]:
            s = -s
    return s

s = sum(1, 2, 3, 4, 5)            # returns 15
s = sum(1, 2, 3, 4, 5, neg=True)  # returns -15
s = sum(1, 2, 3, 4, 5, neg=False) # returns 15

The single star * unpacks the sequence/collection into positional arguments, so you can do this:

def sum(a, b):
    return a + b

values = (1, 2)

s = sum(*values)

This will unpack the tuple so that it actually executes as:

s = sum(1, 2)

The double star ** does the same, only using a dictionary and thus named arguments:

values = { 'a': 1, 'b': 2 }
s = sum(**values)

You can also combine:

def sum(a, b, c, d):
    return a + b + c + d

values1 = (1, 2)
values2 = { 'c': 10, 'd': 15 }
s = sum(*values1, **values2)

will execute as:

s = sum(1, 2, c=10, d=15)

Also see section 4.7.4 – Unpacking Argument Lists of the Python documentation.


Additionally you can define functions to take *x and **y arguments, this allows a function to accept any number of positional and/or named arguments that aren’t specifically named in the declaration.

Example:

def sum(*values):
    s = 0
    for v in values:
        s = s + v
    return s

s = sum(1, 2, 3, 4, 5)

or with **:

def get_a(**values):
    return values['a']

s = get_a(a=1, b=2)      # returns 1

this can allow you to specify a large number of optional parameters without having to declare them.

And again, you can combine:

def sum(*values, **options):
    s = 0
    for i in values:
        s = s + i
    if "neg" in options:
        if options["neg"]:
            s = -s
    return s

s = sum(1, 2, 3, 4, 5)            # returns 15
s = sum(1, 2, 3, 4, 5, neg=True)  # returns -15
s = sum(1, 2, 3, 4, 5, neg=False) # returns 15

回答 1

一点:这些不是运算符。表达式中使用运算符从现有值创建新值(例如1 + 2变为3。这里的*和**是函数声明和调用语法的一部分。

One small point: these are not operators. Operators are used in expressions to create new values from existing values (1+2 becomes 3, for example. The * and ** here are part of the syntax of function declarations and calls.


回答 2

对于要“存储”函数调用的情况,我发现这特别有用。

例如,假设我对功能“ add”进行了一些单元测试:

def add(a, b): return a + b
tests = { (1,4):5, (0, 0):0, (-1, 3):3 }
for test, result in tests.items():
   print 'test: adding', test, '==', result, '---', add(*test) == result

除了手动执行类似add(test [0],test [1])之类的丑陋操作外,没有其他方法可以调用add。另外,如果变量数量可变,则所有需要的if语句的代码都会变得很丑陋。

另一个有用的地方是定义Factory对象(为您创建对象的对象)。假设您有一些工厂类,该类使Car对象返回。您可以使myFactory.make_car(’red’,’bmw’,’335ix’)创建Car(’red’,’bmw’,’335ix’),然后返回它。

def make_car(*args):
   return Car(*args)

当您要调用超类的构造函数时,这也很有用。

I find this particularly useful for when you want to ‘store’ a function call.

For example, suppose I have some unit tests for a function ‘add’:

def add(a, b): return a + b
tests = { (1,4):5, (0, 0):0, (-1, 3):3 }
for test, result in tests.items():
   print 'test: adding', test, '==', result, '---', add(*test) == result

There is no other way to call add, other than manually doing something like add(test[0], test[1]), which is ugly. Also, if there are a variable number of variables, the code could get pretty ugly with all the if-statements you would need.

Another place this is useful is for defining Factory objects (objects that create objects for you). Suppose you have some class Factory, that makes Car objects and returns them. You could make it so that myFactory.make_car(‘red’, ‘bmw’, ‘335ix’) creates Car(‘red’, ‘bmw’, ‘335ix’), then returns it.

def make_car(*args):
   return Car(*args)

This is also useful when you want to call a superclass’ constructor.


回答 3

它称为扩展调用语法。从文档中

如果语法* expression出现在函数调用中,则表达式必须计算为序列。来自此序列的元素被视为它们是附加的位置参数。如果存在位置参数x1,…,xN,并且表达式的计算结果为序列y1,…,yM,则等效于使用M + N个位置参数x1,…,xN,y1,…的调用。 ..,yM。

和:

如果语法** expression出现在函数调用中,则expression必须计算为一个映射,该映射的内容被视为其他关键字参数。如果关键字同时出现在表达式中并作为显式关键字参数出现,则会引发TypeError异常。

It is called the extended call syntax. From the documentation:

If the syntax *expression appears in the function call, expression must evaluate to a sequence. Elements from this sequence are treated as if they were additional positional arguments; if there are positional arguments x1,…, xN, and expression evaluates to a sequence y1, …, yM, this is equivalent to a call with M+N positional arguments x1, …, xN, y1, …, yM.

and:

If the syntax **expression appears in the function call, expression must evaluate to a mapping, the contents of which are treated as additional keyword arguments. In the case of a keyword appearing in both expression and as an explicit keyword argument, a TypeError exception is raised.


回答 4

在函数调用中,单星号将列表变成单独的参数(例如zip(*x),与zip(x1,x2,x3)if相同x=[x1,x2,x3]),而双星号将字典变成单独的关键字参数(例如f(**k),与f(x=my_x, y=my_y)if相同)k = {'x':my_x, 'y':my_y}

在函数定义中,反之亦然:单星将任意数量的参数转换为列表,而双引号将任意数量的关键字参数转换为字典。例如,def foo(*x)表示“ foo接受任意数量的参数,可以通过列表x来访问它们(即,如果用户调用foo(1,2,3)x将是[1,2,3])”,并且def bar(**k)表示“ bar可以接受任意数量的关键字参数,并且可以通过字典k进行访问。 (即,如果用户调用bar(x=42, y=23)k将为{'x': 42, 'y': 23})”。

In a function call the single star turns a list into seperate arguments (e.g. zip(*x) is the same as zip(x1,x2,x3) if x=[x1,x2,x3]) and the double star turns a dictionary into seperate keyword arguments (e.g. f(**k) is the same as f(x=my_x, y=my_y) if k = {'x':my_x, 'y':my_y}.

In a function definition it’s the other way around: the single star turns an arbitrary number of arguments into a list, and the double start turns an arbitrary number of keyword arguments into a dictionary. E.g. def foo(*x) means “foo takes an arbitrary number of arguments and they will be accessible through the list x (i.e. if the user calls foo(1,2,3), x will be [1,2,3])” and def bar(**k) means “bar takes an arbitrary number of keyword arguments and they will be accessible through the dictionary k (i.e. if the user calls bar(x=42, y=23), k will be {'x': 42, 'y': 23})”.


将元组扩展为参数

问题:将元组扩展为参数

有没有一种方法可以将Python元组扩展为函数-作为实际参数?

例如,这里expand()做了魔术:

some_tuple = (1, "foo", "bar")

def myfun(number, str1, str2):
    return (number * 2, str1 + str2, str2 + str1)

myfun(expand(some_tuple)) # (2, "foobar", "barfoo")

我知道可以将其定义myfunmyfun((a, b, c)),但是当然可能会有遗留代码。谢谢

Is there a way to expand a Python tuple into a function – as actual parameters?

For example, here expand() does the magic:

some_tuple = (1, "foo", "bar")

def myfun(number, str1, str2):
    return (number * 2, str1 + str2, str2 + str1)

myfun(expand(some_tuple)) # (2, "foobar", "barfoo")

I know one could define myfun as myfun((a, b, c)), but of course there may be legacy code. Thanks


回答 0

myfun(*some_tuple)完全符合您的要求。的*操作者只需解包元组(或任何可迭代),并把它们作为位置函数的自变量。阅读有关解压缩参数的更多信息。

myfun(*some_tuple) does exactly what you request. The * operator simply unpacks the tuple (or any iterable) and passes them as the positional arguments to the function. Read more about unpacking arguments.


回答 1

请注意,您还可以扩展参数列表的一部分:

myfun(1, *("foo", "bar"))

Note that you can also expand part of argument list:

myfun(1, *("foo", "bar"))

回答 2

看一下Python教程的第4.7.3和4.7.4节。它讨论将元组作为参数传递。

我还将考虑使用命名参数(并传递字典),而不是使用元组并传递序列。当位置不直观或有多个参数时,我发现使用位置参数是一种不好的做法。

Take a look at the Python tutorial section 4.7.3 and 4.7.4. It talks about passing tuples as arguments.

I would also consider using named parameters (and passing a dictionary) instead of using a tuple and passing a sequence. I find the use of positional arguments to be a bad practice when the positions are not intuitive or there are multiple parameters.


回答 3

这是功能编程方法。它从语法糖中提升了元组扩展功能:

apply_tuple = lambda f, t: f(*t)

用法示例:

from toolz import * 
from operator import add, eq

apply_tuple = curry(apply_tuple)

thread_last(
    [(1,2), (3,4)],
    (map, apply_tuple(add)),
    list,
    (eq, [3, 7])
)
# Prints 'True'

咖喱的redefiniton apply_tuple节省了大量的partial,从长远来看通话。

This is the functional programming method. It lifts the tuple expansion feature out of syntax sugar:

apply_tuple = lambda f, t: f(*t)

Example usage:

from toolz import * 
from operator import add, eq

apply_tuple = curry(apply_tuple)

thread_last(
    [(1,2), (3,4)],
    (map, apply_tuple(add)),
    list,
    (eq, [3, 7])
)
# Prints 'True'

curry redefiniton of apply_tuple saves a lot of partial calls in the long run.


**(双星号/星号)和*(星号/星号)对参数有什么作用?

问题:**(双星号/星号)和*(星号/星号)对参数有什么作用?

在以下方法定义中,***param2什么?

def foo(param1, *param2):
def bar(param1, **param2):

In the following method definitions, what does the * and ** do for param2?

def foo(param1, *param2):
def bar(param1, **param2):

回答 0

*args**kwargs是一种常见的成语,以允许参数,以作为部分所述功能的任意数量的多个上定义函数 Python文档英寸

*args给你的所有函数参数为一个元组

def foo(*args):
    for a in args:
        print(a)        

foo(1)
# 1

foo(1,2,3)
# 1
# 2
# 3

**kwargs会给你所有的 关键字参数除了那些与作为字典的形式参数。

def bar(**kwargs):
    for a in kwargs:
        print(a, kwargs[a])  

bar(name='one', age=27)
# age 27
# name one

这两个习惯用法都可以与普通参数混合使用,以允许使用一组固定参数和一些可变参数:

def foo(kind, *args, **kwargs):
   pass

也可以以其他方式使用此方法:

def foo(a, b, c):
    print(a, b, c)

obj = {'b':10, 'c':'lee'}

foo(100,**obj)
# 100 10 lee

*l习惯用法的另一种用法是在调用函数时解压缩参数列表

def foo(bar, lee):
    print(bar, lee)

l = [1,2]

foo(*l)
# 1 2

在Python 3中,可以*l在分配的左侧使用(Extended Iterable Unpacking),尽管在这种情况下它提供的是列表而不是元组:

first, *rest = [1,2,3,4]
first, *l, last = [1,2,3,4]

Python 3还添加了新的语义(请参阅PEP 3102):

def func(arg1, arg2, arg3, *, kwarg1, kwarg2):
    pass

该函数仅接受3个位置参数,之后的所有内容*只能作为关键字参数传递。

The *args and **kwargs is a common idiom to allow arbitrary number of arguments to functions as described in the section more on defining functions in the Python documentation.

The *args will give you all function parameters as a tuple:

def foo(*args):
    for a in args:
        print(a)        

foo(1)
# 1

foo(1,2,3)
# 1
# 2
# 3

The **kwargs will give you all keyword arguments except for those corresponding to a formal parameter as a dictionary.

def bar(**kwargs):
    for a in kwargs:
        print(a, kwargs[a])  

bar(name='one', age=27)
# age 27
# name one

Both idioms can be mixed with normal arguments to allow a set of fixed and some variable arguments:

def foo(kind, *args, **kwargs):
   pass

It is also possible to use this the other way around:

def foo(a, b, c):
    print(a, b, c)

obj = {'b':10, 'c':'lee'}

foo(100,**obj)
# 100 10 lee

Another usage of the *l idiom is to unpack argument lists when calling a function.

def foo(bar, lee):
    print(bar, lee)

l = [1,2]

foo(*l)
# 1 2

In Python 3 it is possible to use *l on the left side of an assignment (Extended Iterable Unpacking), though it gives a list instead of a tuple in this context:

first, *rest = [1,2,3,4]
first, *l, last = [1,2,3,4]

Also Python 3 adds new semantic (refer PEP 3102):

def func(arg1, arg2, arg3, *, kwarg1, kwarg2):
    pass

Such function accepts only 3 positional arguments, and everything after * can only be passed as keyword arguments.


回答 1

另外值得一提的是,你可以使用***调用功能,以及时。这是一个快捷方式,允许您使用列表/元组或字典将多个参数直接传递给函数。例如,如果您具有以下功能:

def foo(x,y,z):
    print("x=" + str(x))
    print("y=" + str(y))
    print("z=" + str(z))

您可以执行以下操作:

>>> mylist = [1,2,3]
>>> foo(*mylist)
x=1
y=2
z=3

>>> mydict = {'x':1,'y':2,'z':3}
>>> foo(**mydict)
x=1
y=2
z=3

>>> mytuple = (1, 2, 3)
>>> foo(*mytuple)
x=1
y=2
z=3

注意:中的键mydict必须完全像function参数一样命名foo。否则会抛出TypeError

>>> mydict = {'x':1,'y':2,'z':3,'badnews':9}
>>> foo(**mydict)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: foo() got an unexpected keyword argument 'badnews'

It’s also worth noting that you can use * and ** when calling functions as well. This is a shortcut that allows you to pass multiple arguments to a function directly using either a list/tuple or a dictionary. For example, if you have the following function:

def foo(x,y,z):
    print("x=" + str(x))
    print("y=" + str(y))
    print("z=" + str(z))

You can do things like:

>>> mylist = [1,2,3]
>>> foo(*mylist)
x=1
y=2
z=3

>>> mydict = {'x':1,'y':2,'z':3}
>>> foo(**mydict)
x=1
y=2
z=3

>>> mytuple = (1, 2, 3)
>>> foo(*mytuple)
x=1
y=2
z=3

Note: The keys in mydict have to be named exactly like the parameters of function foo. Otherwise it will throw a TypeError:

>>> mydict = {'x':1,'y':2,'z':3,'badnews':9}
>>> foo(**mydict)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: foo() got an unexpected keyword argument 'badnews'

回答 2

单个*表示可以有任意数量的额外位置参数。foo()可以像这样调用foo(1,2,3,4,5)。在foo()主体中,param2是一个包含2-5的序列。

双**表示可以有任意数量的额外命名参数。bar()可以像这样调用bar(1, a=2, b=3)。在bar()的主体中,param2是一个包含{‘a’:2,’b’:3}的字典。

使用以下代码:

def foo(param1, *param2):
    print(param1)
    print(param2)

def bar(param1, **param2):
    print(param1)
    print(param2)

foo(1,2,3,4,5)
bar(1,a=2,b=3)

输出是

1
(2, 3, 4, 5)
1
{'a': 2, 'b': 3}

The single * means that there can be any number of extra positional arguments. foo() can be invoked like foo(1,2,3,4,5). In the body of foo() param2 is a sequence containing 2-5.

The double ** means there can be any number of extra named parameters. bar() can be invoked like bar(1, a=2, b=3). In the body of bar() param2 is a dictionary containing {‘a’:2, ‘b’:3 }

With the following code:

def foo(param1, *param2):
    print(param1)
    print(param2)

def bar(param1, **param2):
    print(param1)
    print(param2)

foo(1,2,3,4,5)
bar(1,a=2,b=3)

the output is

1
(2, 3, 4, 5)
1
{'a': 2, 'b': 3}

回答 3

这是什么**(双星)和*(明星)的参数做

它们允许定义函数以接受并允许用户传递任意数量的参数,位置(*)和关键字(**)。

定义功能

*args允许任意数量的可选位置参数(参数),这些参数将分配给名为的元组args

**kwargs允许任意数量的可选关键字参数(参数),这些参数将位于名为的字典中kwargs

您可以(并且应该)选择任何适当的名称,但是如果目的是使参数具有非特定的语义,args并且kwargs是标准名称。

扩展,传递任意数量的参数

您还可以分别使用*args**kwargs传入列表(或任何可迭代的)和字典(或任何映射)的参数。

接收参数的函数不必知道它们正在扩展。

例如,Python 2的xrange并不明确期望*args,但是因为它使用3个整数作为参数:

>>> x = xrange(3) # create our *args - an iterable of 3 integers
>>> xrange(*x)    # expand here
xrange(0, 2, 2)

再举一个例子,我们可以在下面使用dict扩展str.format

>>> foo = 'FOO'
>>> bar = 'BAR'
>>> 'this is foo, {foo} and bar, {bar}'.format(**locals())
'this is foo, FOO and bar, BAR'

Python 3的新功能:使用仅关键字参数定义函数

您可以在- 之后添加仅关键字参数*args -例如,在此处,kwarg2必须将其作为关键字参数-而不是位置:

def foo(arg, kwarg=None, *args, kwarg2=None, **kwargs): 
    return arg, kwarg, args, kwarg2, kwargs

用法:

>>> foo(1,2,3,4,5,kwarg2='kwarg2', bar='bar', baz='baz')
(1, 2, (3, 4, 5), 'kwarg2', {'bar': 'bar', 'baz': 'baz'})

同样,*可以单独用于表示仅关键字参数跟随,而不允许无限的位置参数。

def foo(arg, kwarg=None, *, kwarg2=None, **kwargs): 
    return arg, kwarg, kwarg2, kwargs

在这里,kwarg2再次必须是一个明确命名的关键字参数:

>>> foo(1,2,kwarg2='kwarg2', foo='foo', bar='bar')
(1, 2, 'kwarg2', {'foo': 'foo', 'bar': 'bar'})

而且我们不再可以接受无限的位置参数,因为我们没有*args*

>>> foo(1,2,3,4,5, kwarg2='kwarg2', foo='foo', bar='bar')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: foo() takes from 1 to 2 positional arguments 
    but 5 positional arguments (and 1 keyword-only argument) were given

再次,更简单地说,在这里我们需要kwarg使用名称,而不是位置:

def bar(*, kwarg=None): 
    return kwarg

在此示例中,我们看到如果尝试通过kwarg位置传递,则会收到错误消息:

>>> bar('kwarg')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: bar() takes 0 positional arguments but 1 was given

我们必须显式传递kwarg参数作为关键字参数。

>>> bar(kwarg='kwarg')
'kwarg'

兼容Python 2的演示

*args(通常说“ star-args”)和**kwargs(可以通过说“ kwargs”来暗示星号,但是用“ double-star kwargs”来明确表示)是使用***表示法的Python的常见用法。这些特定的变量名称不是必需的(例如,您可以使用*foos**bars),但是背离约定可能会激怒您的Python编码人员。

当我们不知道函数将要接收什么或我们可能传递多少个参数时,我们通常会使用它们,有时甚至即使分别命名每个变量也会变得非常混乱和多余(但这是通常显式的情况比隐式更好)。

例子1

以下功能描述了如何使用它们,并演示了行为。请注意,命名b参数将由前面的第二个位置参数使用:

def foo(a, b=10, *args, **kwargs):
    '''
    this function takes required argument a, not required keyword argument b
    and any number of unknown positional arguments and keyword arguments after
    '''
    print('a is a required argument, and its value is {0}'.format(a))
    print('b not required, its default value is 10, actual value: {0}'.format(b))
    # we can inspect the unknown arguments we were passed:
    #  - args:
    print('args is of type {0} and length {1}'.format(type(args), len(args)))
    for arg in args:
        print('unknown arg: {0}'.format(arg))
    #  - kwargs:
    print('kwargs is of type {0} and length {1}'.format(type(kwargs),
                                                        len(kwargs)))
    for kw, arg in kwargs.items():
        print('unknown kwarg - kw: {0}, arg: {1}'.format(kw, arg))
    # But we don't have to know anything about them 
    # to pass them to other functions.
    print('Args or kwargs can be passed without knowing what they are.')
    # max can take two or more positional args: max(a, b, c...)
    print('e.g. max(a, b, *args) \n{0}'.format(
      max(a, b, *args))) 
    kweg = 'dict({0})'.format( # named args same as unknown kwargs
      ', '.join('{k}={v}'.format(k=k, v=v) 
                             for k, v in sorted(kwargs.items())))
    print('e.g. dict(**kwargs) (same as {kweg}) returns: \n{0}'.format(
      dict(**kwargs), kweg=kweg))

我们可以检查函数的签名的在线帮助,以help(foo),它告诉我们

foo(a, b=10, *args, **kwargs)

让我们用 foo(1, 2, 3, 4, e=5, f=6, g=7)

打印:

a is a required argument, and its value is 1
b not required, its default value is 10, actual value: 2
args is of type <type 'tuple'> and length 2
unknown arg: 3
unknown arg: 4
kwargs is of type <type 'dict'> and length 3
unknown kwarg - kw: e, arg: 5
unknown kwarg - kw: g, arg: 7
unknown kwarg - kw: f, arg: 6
Args or kwargs can be passed without knowing what they are.
e.g. max(a, b, *args) 
4
e.g. dict(**kwargs) (same as dict(e=5, f=6, g=7)) returns: 
{'e': 5, 'g': 7, 'f': 6}

例子2

我们还可以使用另一个函数来调用它a

def bar(a):
    b, c, d, e, f = 2, 3, 4, 5, 6
    # dumping every local variable into foo as a keyword argument 
    # by expanding the locals dict:
    foo(**locals()) 

bar(100) 印刷品:

a is a required argument, and its value is 100
b not required, its default value is 10, actual value: 2
args is of type <type 'tuple'> and length 0
kwargs is of type <type 'dict'> and length 4
unknown kwarg - kw: c, arg: 3
unknown kwarg - kw: e, arg: 5
unknown kwarg - kw: d, arg: 4
unknown kwarg - kw: f, arg: 6
Args or kwargs can be passed without knowing what they are.
e.g. max(a, b, *args) 
100
e.g. dict(**kwargs) (same as dict(c=3, d=4, e=5, f=6)) returns: 
{'c': 3, 'e': 5, 'd': 4, 'f': 6}

示例3:装饰器中的实际用法

好的,所以也许我们还没有看到该实用程序。因此,假设您在区分代码之前和/或之后有多个带有冗余代码的功能。为了说明的目的,以下命名函数只是伪代码。

def foo(a, b, c, d=0, e=100):
    # imagine this is much more code than a simple function call
    preprocess() 
    differentiating_process_foo(a,b,c,d,e)
    # imagine this is much more code than a simple function call
    postprocess()

def bar(a, b, c=None, d=0, e=100, f=None):
    preprocess()
    differentiating_process_bar(a,b,c,d,e,f)
    postprocess()

def baz(a, b, c, d, e, f):
    ... and so on

我们也许可以用不同的方式处理此问题,但是我们当然可以用装饰器提取冗余,因此下面的示例演示了如何*args并且**kwargs非常有用:

def decorator(function):
    '''function to wrap other functions with a pre- and postprocess'''
    @functools.wraps(function) # applies module, name, and docstring to wrapper
    def wrapper(*args, **kwargs):
        # again, imagine this is complicated, but we only write it once!
        preprocess()
        function(*args, **kwargs)
        postprocess()
    return wrapper

现在,由于我们考虑了冗余性,每个包装函数都可以更加简洁地编写:

@decorator
def foo(a, b, c, d=0, e=100):
    differentiating_process_foo(a,b,c,d,e)

@decorator
def bar(a, b, c=None, d=0, e=100, f=None):
    differentiating_process_bar(a,b,c,d,e,f)

@decorator
def baz(a, b, c=None, d=0, e=100, f=None, g=None):
    differentiating_process_baz(a,b,c,d,e,f, g)

@decorator
def quux(a, b, c=None, d=0, e=100, f=None, g=None, h=None):
    differentiating_process_quux(a,b,c,d,e,f,g,h)

通过分解*args**kwargs允许我们这样做的代码,我们减少了代码行,提高了可读性和可维护性,并且在程序中具有唯一的规范逻辑位置。如果需要更改此结构的任何部分,则可以在一个位置进行每次更改。

What does ** (double star) and * (star) do for parameters

They allow for functions to be defined to accept and for users to pass any number of arguments, positional (*) and keyword (**).

Defining Functions

*args allows for any number of optional positional arguments (parameters), which will be assigned to a tuple named args.

**kwargs allows for any number of optional keyword arguments (parameters), which will be in a dict named kwargs.

You can (and should) choose any appropriate name, but if the intention is for the arguments to be of non-specific semantics, args and kwargs are standard names.

Expansion, Passing any number of arguments

You can also use *args and **kwargs to pass in parameters from lists (or any iterable) and dicts (or any mapping), respectively.

The function recieving the parameters does not have to know that they are being expanded.

For example, Python 2’s xrange does not explicitly expect *args, but since it takes 3 integers as arguments:

>>> x = xrange(3) # create our *args - an iterable of 3 integers
>>> xrange(*x)    # expand here
xrange(0, 2, 2)

As another example, we can use dict expansion in str.format:

>>> foo = 'FOO'
>>> bar = 'BAR'
>>> 'this is foo, {foo} and bar, {bar}'.format(**locals())
'this is foo, FOO and bar, BAR'

New in Python 3: Defining functions with keyword only arguments

You can have keyword only arguments after the *args – for example, here, kwarg2 must be given as a keyword argument – not positionally:

def foo(arg, kwarg=None, *args, kwarg2=None, **kwargs): 
    return arg, kwarg, args, kwarg2, kwargs

Usage:

>>> foo(1,2,3,4,5,kwarg2='kwarg2', bar='bar', baz='baz')
(1, 2, (3, 4, 5), 'kwarg2', {'bar': 'bar', 'baz': 'baz'})

Also, * can be used by itself to indicate that keyword only arguments follow, without allowing for unlimited positional arguments.

def foo(arg, kwarg=None, *, kwarg2=None, **kwargs): 
    return arg, kwarg, kwarg2, kwargs

Here, kwarg2 again must be an explicitly named, keyword argument:

>>> foo(1,2,kwarg2='kwarg2', foo='foo', bar='bar')
(1, 2, 'kwarg2', {'foo': 'foo', 'bar': 'bar'})

And we can no longer accept unlimited positional arguments because we don’t have *args*:

>>> foo(1,2,3,4,5, kwarg2='kwarg2', foo='foo', bar='bar')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: foo() takes from 1 to 2 positional arguments 
    but 5 positional arguments (and 1 keyword-only argument) were given

Again, more simply, here we require kwarg to be given by name, not positionally:

def bar(*, kwarg=None): 
    return kwarg

In this example, we see that if we try to pass kwarg positionally, we get an error:

>>> bar('kwarg')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: bar() takes 0 positional arguments but 1 was given

We must explicitly pass the kwarg parameter as a keyword argument.

>>> bar(kwarg='kwarg')
'kwarg'

Python 2 compatible demos

*args (typically said “star-args”) and **kwargs (stars can be implied by saying “kwargs”, but be explicit with “double-star kwargs”) are common idioms of Python for using the * and ** notation. These specific variable names aren’t required (e.g. you could use *foos and **bars), but a departure from convention is likely to enrage your fellow Python coders.

We typically use these when we don’t know what our function is going to receive or how many arguments we may be passing, and sometimes even when naming every variable separately would get very messy and redundant (but this is a case where usually explicit is better than implicit).

Example 1

The following function describes how they can be used, and demonstrates behavior. Note the named b argument will be consumed by the second positional argument before :

def foo(a, b=10, *args, **kwargs):
    '''
    this function takes required argument a, not required keyword argument b
    and any number of unknown positional arguments and keyword arguments after
    '''
    print('a is a required argument, and its value is {0}'.format(a))
    print('b not required, its default value is 10, actual value: {0}'.format(b))
    # we can inspect the unknown arguments we were passed:
    #  - args:
    print('args is of type {0} and length {1}'.format(type(args), len(args)))
    for arg in args:
        print('unknown arg: {0}'.format(arg))
    #  - kwargs:
    print('kwargs is of type {0} and length {1}'.format(type(kwargs),
                                                        len(kwargs)))
    for kw, arg in kwargs.items():
        print('unknown kwarg - kw: {0}, arg: {1}'.format(kw, arg))
    # But we don't have to know anything about them 
    # to pass them to other functions.
    print('Args or kwargs can be passed without knowing what they are.')
    # max can take two or more positional args: max(a, b, c...)
    print('e.g. max(a, b, *args) \n{0}'.format(
      max(a, b, *args))) 
    kweg = 'dict({0})'.format( # named args same as unknown kwargs
      ', '.join('{k}={v}'.format(k=k, v=v) 
                             for k, v in sorted(kwargs.items())))
    print('e.g. dict(**kwargs) (same as {kweg}) returns: \n{0}'.format(
      dict(**kwargs), kweg=kweg))

We can check the online help for the function’s signature, with help(foo), which tells us

foo(a, b=10, *args, **kwargs)

Let’s call this function with foo(1, 2, 3, 4, e=5, f=6, g=7)

which prints:

a is a required argument, and its value is 1
b not required, its default value is 10, actual value: 2
args is of type <type 'tuple'> and length 2
unknown arg: 3
unknown arg: 4
kwargs is of type <type 'dict'> and length 3
unknown kwarg - kw: e, arg: 5
unknown kwarg - kw: g, arg: 7
unknown kwarg - kw: f, arg: 6
Args or kwargs can be passed without knowing what they are.
e.g. max(a, b, *args) 
4
e.g. dict(**kwargs) (same as dict(e=5, f=6, g=7)) returns: 
{'e': 5, 'g': 7, 'f': 6}

Example 2

We can also call it using another function, into which we just provide a:

def bar(a):
    b, c, d, e, f = 2, 3, 4, 5, 6
    # dumping every local variable into foo as a keyword argument 
    # by expanding the locals dict:
    foo(**locals()) 

bar(100) prints:

a is a required argument, and its value is 100
b not required, its default value is 10, actual value: 2
args is of type <type 'tuple'> and length 0
kwargs is of type <type 'dict'> and length 4
unknown kwarg - kw: c, arg: 3
unknown kwarg - kw: e, arg: 5
unknown kwarg - kw: d, arg: 4
unknown kwarg - kw: f, arg: 6
Args or kwargs can be passed without knowing what they are.
e.g. max(a, b, *args) 
100
e.g. dict(**kwargs) (same as dict(c=3, d=4, e=5, f=6)) returns: 
{'c': 3, 'e': 5, 'd': 4, 'f': 6}

Example 3: practical usage in decorators

OK, so maybe we’re not seeing the utility yet. So imagine you have several functions with redundant code before and/or after the differentiating code. The following named functions are just pseudo-code for illustrative purposes.

def foo(a, b, c, d=0, e=100):
    # imagine this is much more code than a simple function call
    preprocess() 
    differentiating_process_foo(a,b,c,d,e)
    # imagine this is much more code than a simple function call
    postprocess()

def bar(a, b, c=None, d=0, e=100, f=None):
    preprocess()
    differentiating_process_bar(a,b,c,d,e,f)
    postprocess()

def baz(a, b, c, d, e, f):
    ... and so on

We might be able to handle this differently, but we can certainly extract the redundancy with a decorator, and so our below example demonstrates how *args and **kwargs can be very useful:

def decorator(function):
    '''function to wrap other functions with a pre- and postprocess'''
    @functools.wraps(function) # applies module, name, and docstring to wrapper
    def wrapper(*args, **kwargs):
        # again, imagine this is complicated, but we only write it once!
        preprocess()
        function(*args, **kwargs)
        postprocess()
    return wrapper

And now every wrapped function can be written much more succinctly, as we’ve factored out the redundancy:

@decorator
def foo(a, b, c, d=0, e=100):
    differentiating_process_foo(a,b,c,d,e)

@decorator
def bar(a, b, c=None, d=0, e=100, f=None):
    differentiating_process_bar(a,b,c,d,e,f)

@decorator
def baz(a, b, c=None, d=0, e=100, f=None, g=None):
    differentiating_process_baz(a,b,c,d,e,f, g)

@decorator
def quux(a, b, c=None, d=0, e=100, f=None, g=None, h=None):
    differentiating_process_quux(a,b,c,d,e,f,g,h)

And by factoring out our code, which *args and **kwargs allows us to do, we reduce lines of code, improve readability and maintainability, and have sole canonical locations for the logic in our program. If we need to change any part of this structure, we have one place in which to make each change.


回答 4

首先让我们了解什么是位置参数和关键字参数。下面是带有位置参数的函数定义的示例

def test(a,b,c):
     print(a)
     print(b)
     print(c)

test(1,2,3)
#output:
1
2
3

因此,这是带有位置参数的函数定义。您也可以使用关键字/命名参数来调用它:

def test(a,b,c):
     print(a)
     print(b)
     print(c)

test(a=1,b=2,c=3)
#output:
1
2
3

现在让我们研究一个带有关键字参数的函数定义示例:

def test(a=0,b=0,c=0):
     print(a)
     print(b)
     print(c)
     print('-------------------------')

test(a=1,b=2,c=3)
#output :
1
2
3
-------------------------

您也可以使用位置参数调用此函数:

def test(a=0,b=0,c=0):
    print(a)
    print(b)
    print(c)
    print('-------------------------')

test(1,2,3)
# output :
1
2
3
---------------------------------

因此,我们现在知道带有位置参数以及关键字参数的函数定义。

现在让我们研究“ *”运算符和“ **”运算符。

请注意,这些运算符可以在两个区域中使用:

a)函数调用

b)功能定义

函数调用中使用“ *”运算符和“ **”运算符

让我们直接看一个例子,然后讨论它。

def sum(a,b):  #receive args from function calls as sum(1,2) or sum(a=1,b=2)
    print(a+b)

my_tuple = (1,2)
my_list = [1,2]
my_dict = {'a':1,'b':2}

# Let us unpack data structure of list or tuple or dict into arguments with help of '*' operator
sum(*my_tuple)   # becomes same as sum(1,2) after unpacking my_tuple with '*'
sum(*my_list)    # becomes same as sum(1,2) after unpacking my_list with  '*'
sum(**my_dict)   # becomes same as sum(a=1,b=2) after unpacking by '**' 

# output is 3 in all three calls to sum function.

所以记住

函数调用中使用“ *”或“ **”运算符时-

‘*’运算符将列表或元组等数据结构解压缩为函数定义所需的参数。

‘**’运算符将字典分解成函数定义所需的参数。

现在让我们研究函数定义中使用’*’运算符。例:

def sum(*args): #pack the received positional args into data structure of tuple. after applying '*' - def sum((1,2,3,4))
    sum = 0
    for a in args:
        sum+=a
    print(sum)

sum(1,2,3,4)  #positional args sent to function sum
#output:
10

在函数定义中,“ *”运算符将接收到的参数打包到一个元组中。

现在让我们看一下函数定义中使用的“ **”示例:

def sum(**args): #pack keyword args into datastructure of dict after applying '**' - def sum({a:1,b:2,c:3,d:4})
    sum=0
    for k,v in args.items():
        sum+=v
    print(sum)

sum(a=1,b=2,c=3,d=4) #positional args sent to function sum

在函数定义中,“ **”运算符将接收到的参数打包到字典中。

因此请记住:

函数调用中,“ *” 将元组或列表的数据结构解压缩为位置或关键字参数,以供函数定义接收。

函数调用中,“ **” 将字典的数据结构解压缩为位置或关键字参数,以供函数定义接收。

函数定义中,“ *” 位置参数打包到元组中。

函数定义中,“ **” 关键字参数打包到字典中。

Let us first understand what are positional arguments and keyword arguments. Below is an example of function definition with Positional arguments.

def test(a,b,c):
     print(a)
     print(b)
     print(c)

test(1,2,3)
#output:
1
2
3

So this is a function definition with positional arguments. You can call it with keyword/named arguments as well:

def test(a,b,c):
     print(a)
     print(b)
     print(c)

test(a=1,b=2,c=3)
#output:
1
2
3

Now let us study an example of function definition with keyword arguments:

def test(a=0,b=0,c=0):
     print(a)
     print(b)
     print(c)
     print('-------------------------')

test(a=1,b=2,c=3)
#output :
1
2
3
-------------------------

You can call this function with positional arguments as well:

def test(a=0,b=0,c=0):
    print(a)
    print(b)
    print(c)
    print('-------------------------')

test(1,2,3)
# output :
1
2
3
---------------------------------

So we now know function definitions with positional as well as keyword arguments.

Now let us study the ‘*’ operator and ‘**’ operator.

Please note these operators can be used in 2 areas:

a) function call

b) function definition

The use of ‘*’ operator and ‘**’ operator in function call.

Let us get straight to an example and then discuss it.

def sum(a,b):  #receive args from function calls as sum(1,2) or sum(a=1,b=2)
    print(a+b)

my_tuple = (1,2)
my_list = [1,2]
my_dict = {'a':1,'b':2}

# Let us unpack data structure of list or tuple or dict into arguments with help of '*' operator
sum(*my_tuple)   # becomes same as sum(1,2) after unpacking my_tuple with '*'
sum(*my_list)    # becomes same as sum(1,2) after unpacking my_list with  '*'
sum(**my_dict)   # becomes same as sum(a=1,b=2) after unpacking by '**' 

# output is 3 in all three calls to sum function.

So remember

when the ‘*’ or ‘**’ operator is used in a function call

‘*’ operator unpacks data structure such as a list or tuple into arguments needed by function definition.

‘**’ operator unpacks a dictionary into arguments needed by function definition.

Now let us study the ‘*’ operator use in function definition. Example:

def sum(*args): #pack the received positional args into data structure of tuple. after applying '*' - def sum((1,2,3,4))
    sum = 0
    for a in args:
        sum+=a
    print(sum)

sum(1,2,3,4)  #positional args sent to function sum
#output:
10

In function definition the ‘*’ operator packs the received arguments into a tuple.

Now let us see an example of ‘**’ used in function definition:

def sum(**args): #pack keyword args into datastructure of dict after applying '**' - def sum({a:1,b:2,c:3,d:4})
    sum=0
    for k,v in args.items():
        sum+=v
    print(sum)

sum(a=1,b=2,c=3,d=4) #positional args sent to function sum

In function definition The ‘**’ operator packs the received arguments into a dictionary.

So remember:

In a function call the ‘*’ unpacks data structure of tuple or list into positional or keyword arguments to be received by function definition.

In a function call the ‘**’ unpacks data structure of dictionary into positional or keyword arguments to be received by function definition.

In a function definition the ‘*’ packs positional arguments into a tuple.

In a function definition the ‘**’ packs keyword arguments into a dictionary.


回答 5

该表非常适合在函数构造和函数调用中使用*和使用:**

            In function construction         In function call
=======================================================================
          |  def f(*args):                 |  def f(a, b):
*args     |      for arg in args:          |      return a + b
          |          print(arg)            |  args = (1, 2)
          |  f(1, 2)                       |  f(*args)
----------|--------------------------------|---------------------------
          |  def f(a, b):                  |  def f(a, b):
**kwargs  |      return a + b              |      return a + b
          |  def g(**kwargs):              |  kwargs = dict(a=1, b=2)
          |      return f(**kwargs)        |  f(**kwargs)
          |  g(a=1, b=2)                   |
-----------------------------------------------------------------------

这实际上只是用来总结Lorin Hochstein的答案,但我发现它很有帮助。

相关:在Python 3中已扩展了star / splat运算符的用法

This table is handy for using * and ** in function construction and function call:

            In function construction         In function call
=======================================================================
          |  def f(*args):                 |  def f(a, b):
*args     |      for arg in args:          |      return a + b
          |          print(arg)            |  args = (1, 2)
          |  f(1, 2)                       |  f(*args)
----------|--------------------------------|---------------------------
          |  def f(a, b):                  |  def f(a, b):
**kwargs  |      return a + b              |      return a + b
          |  def g(**kwargs):              |  kwargs = dict(a=1, b=2)
          |      return f(**kwargs)        |  f(**kwargs)
          |  g(a=1, b=2)                   |
-----------------------------------------------------------------------

This really just serves to summarize Lorin Hochstein’s answer but I find it helpful.

Relatedly: uses for the star/splat operators have been expanded in Python 3


回答 6

***在函数参数列表中有特殊用法。* 表示该参数是一个列表,并且**表示该参数是一个字典。这允许函数接受任意数量的参数

* and ** have special usage in the function argument list. * implies that the argument is a list and ** implies that the argument is a dictionary. This allows functions to take arbitrary number of arguments


回答 7

对于那些通过榜样学习的人!

  1. 的目的* 是使您能够定义一个函数,该函数可以采用以列表形式提供的任意数量的参数(例如f(*myList))。
  2. 目的**是通过提供字典(例如f(**{'x' : 1, 'y' : 2}))使您能够输入函数的参数。

就让我们一起来通过定义一个函数,它有两个正常的变量xy以及可以接受更多的论据myArgs,并能接受更多的论据myKW。稍后,我们将展示如何y使用进行订阅myArgDict

def f(x, y, *myArgs, **myKW):
    print("# x      = {}".format(x))
    print("# y      = {}".format(y))
    print("# myArgs = {}".format(myArgs))
    print("# myKW   = {}".format(myKW))
    print("# ----------------------------------------------------------------------")

# Define a list for demonstration purposes
myList    = ["Left", "Right", "Up", "Down"]
# Define a dictionary for demonstration purposes
myDict    = {"Wubba": "lubba", "Dub": "dub"}
# Define a dictionary to feed y
myArgDict = {'y': "Why?", 'y0': "Why not?", "q": "Here is a cue!"}

# The 1st elem of myList feeds y
f("myEx", *myList, **myDict)
# x      = myEx
# y      = Left
# myArgs = ('Right', 'Up', 'Down')
# myKW   = {'Wubba': 'lubba', 'Dub': 'dub'}
# ----------------------------------------------------------------------

# y is matched and fed first
# The rest of myArgDict becomes additional arguments feeding myKW
f("myEx", **myArgDict)
# x      = myEx
# y      = Why?
# myArgs = ()
# myKW   = {'y0': 'Why not?', 'q': 'Here is a cue!'}
# ----------------------------------------------------------------------

# The rest of myArgDict becomes additional arguments feeding myArgs
f("myEx", *myArgDict)
# x      = myEx
# y      = y
# myArgs = ('y0', 'q')
# myKW   = {}
# ----------------------------------------------------------------------

# Feed extra arguments manually and append even more from my list
f("myEx", 4, 42, 420, *myList, *myDict, **myDict)
# x      = myEx
# y      = 4
# myArgs = (42, 420, 'Left', 'Right', 'Up', 'Down', 'Wubba', 'Dub')
# myKW   = {'Wubba': 'lubba', 'Dub': 'dub'}
# ----------------------------------------------------------------------

# Without the stars, the entire provided list and dict become x, and y:
f(myList, myDict)
# x      = ['Left', 'Right', 'Up', 'Down']
# y      = {'Wubba': 'lubba', 'Dub': 'dub'}
# myArgs = ()
# myKW   = {}
# ----------------------------------------------------------------------

注意事项

  1. ** 专为字典保留。
  2. 非可选参数分配首先发生。
  3. 您不能两次使用非可选参数。
  4. 如果适用,**必须*始终紧随其后。

For those of you who learn by examples!

  1. The purpose of * is to give you the ability to define a function that can take an arbitrary number of arguments provided as a list (e.g. f(*myList) ).
  2. The purpose of ** is to give you the ability to feed a function’s arguments by providing a dictionary (e.g. f(**{'x' : 1, 'y' : 2}) ).

Let us show this by defining a function that takes two normal variables x, y, and can accept more arguments as myArgs, and can accept even more arguments as myKW. Later, we will show how to feed y using myArgDict.

def f(x, y, *myArgs, **myKW):
    print("# x      = {}".format(x))
    print("# y      = {}".format(y))
    print("# myArgs = {}".format(myArgs))
    print("# myKW   = {}".format(myKW))
    print("# ----------------------------------------------------------------------")

# Define a list for demonstration purposes
myList    = ["Left", "Right", "Up", "Down"]
# Define a dictionary for demonstration purposes
myDict    = {"Wubba": "lubba", "Dub": "dub"}
# Define a dictionary to feed y
myArgDict = {'y': "Why?", 'y0': "Why not?", "q": "Here is a cue!"}

# The 1st elem of myList feeds y
f("myEx", *myList, **myDict)
# x      = myEx
# y      = Left
# myArgs = ('Right', 'Up', 'Down')
# myKW   = {'Wubba': 'lubba', 'Dub': 'dub'}
# ----------------------------------------------------------------------

# y is matched and fed first
# The rest of myArgDict becomes additional arguments feeding myKW
f("myEx", **myArgDict)
# x      = myEx
# y      = Why?
# myArgs = ()
# myKW   = {'y0': 'Why not?', 'q': 'Here is a cue!'}
# ----------------------------------------------------------------------

# The rest of myArgDict becomes additional arguments feeding myArgs
f("myEx", *myArgDict)
# x      = myEx
# y      = y
# myArgs = ('y0', 'q')
# myKW   = {}
# ----------------------------------------------------------------------

# Feed extra arguments manually and append even more from my list
f("myEx", 4, 42, 420, *myList, *myDict, **myDict)
# x      = myEx
# y      = 4
# myArgs = (42, 420, 'Left', 'Right', 'Up', 'Down', 'Wubba', 'Dub')
# myKW   = {'Wubba': 'lubba', 'Dub': 'dub'}
# ----------------------------------------------------------------------

# Without the stars, the entire provided list and dict become x, and y:
f(myList, myDict)
# x      = ['Left', 'Right', 'Up', 'Down']
# y      = {'Wubba': 'lubba', 'Dub': 'dub'}
# myArgs = ()
# myKW   = {}
# ----------------------------------------------------------------------

Caveats

  1. ** is exclusively reserved for dictionaries.
  2. Non-optional argument assignment happens first.
  3. You cannot use a non-optional argument twice.
  4. If applicable, ** must come after *, always.

回答 8

从Python文档中:

如果位置参数多于形式参数槽,则将引发TypeError异常,除非存在使用语法“ * identifier”的形式参数;否则,将引发TypeError异常。在这种情况下,该形式参数会接收包含多余位置参数的元组(如果没有多余位置参数,则为空元组)。

如果任何关键字参数与形式参数名称都不对应,则除非存在使用语法“ ** identifier”的形式参数,否则将引发TypeError异常;否则,将引发TypeError异常。在这种情况下,该形式参数将接收包含多余关键字参数的字典(使用关键字作为键,并将参数值用作对应的值),或者如果没有多余的关键字参数,则接收一个(新的)空字典。

From the Python documentation:

If there are more positional arguments than there are formal parameter slots, a TypeError exception is raised, unless a formal parameter using the syntax “*identifier” is present; in this case, that formal parameter receives a tuple containing the excess positional arguments (or an empty tuple if there were no excess positional arguments).

If any keyword argument does not correspond to a formal parameter name, a TypeError exception is raised, unless a formal parameter using the syntax “**identifier” is present; in this case, that formal parameter receives a dictionary containing the excess keyword arguments (using the keywords as keys and the argument values as corresponding values), or a (new) empty dictionary if there were no excess keyword arguments.


回答 9

* 表示将可变参数作为元组接收

** 表示接收可变参数作为字典

使用方式如下:

1)单*

def foo(*args):
    for arg in args:
        print(arg)

foo("two", 3)

输出:

two
3

2)现在 **

def bar(**kwargs):
    for key in kwargs:
        print(key, kwargs[key])

bar(dic1="two", dic2=3)

输出:

dic1 two
dic2 3

* means receive variable arguments as tuple

** means receive variable arguments as dictionary

Used like the following:

1) single *

def foo(*args):
    for arg in args:
        print(arg)

foo("two", 3)

Output:

two
3

2) Now **

def bar(**kwargs):
    for key in kwargs:
        print(key, kwargs[key])

bar(dic1="two", dic2=3)

Output:

dic1 two
dic2 3

回答 10

我想举一个别人没有提到的例子

*也可以打开生成器包装

Python3文档中的一个示例

x = [1, 2, 3]
y = [4, 5, 6]

unzip_x, unzip_y = zip(*zip(x, y))

unzip_x将为[1、2、3],unzip_y将为[4、5、6]

zip()接收多个可初始化的参数,并返回一个生成器。

zip(*zip(x,y)) -> zip((1, 4), (2, 5), (3, 6))

I want to give an example which others haven’t mentioned

* can also unpack a generator

An example from Python3 Document

x = [1, 2, 3]
y = [4, 5, 6]

unzip_x, unzip_y = zip(*zip(x, y))

unzip_x will be [1, 2, 3], unzip_y will be [4, 5, 6]

The zip() receives multiple iretable args, and return a generator.

zip(*zip(x,y)) -> zip((1, 4), (2, 5), (3, 6))

回答 11

在Python 3.5,你也可以使用这个语法listdicttuple,和set显示器(有时也称为文本)。请参阅PEP 488:其他拆包概述

>>> (0, *range(1, 4), 5, *range(6, 8))
(0, 1, 2, 3, 5, 6, 7)
>>> [0, *range(1, 4), 5, *range(6, 8)]
[0, 1, 2, 3, 5, 6, 7]
>>> {0, *range(1, 4), 5, *range(6, 8)}
{0, 1, 2, 3, 5, 6, 7}
>>> d = {'one': 1, 'two': 2, 'three': 3}
>>> e = {'six': 6, 'seven': 7}
>>> {'zero': 0, **d, 'five': 5, **e}
{'five': 5, 'seven': 7, 'two': 2, 'one': 1, 'three': 3, 'six': 6, 'zero': 0}

它还允许在单个函数调用中解压缩多个可迭代对象。

>>> range(*[1, 10], *[2])
range(1, 10, 2)

(感谢mgilson的PEP链接。)

In Python 3.5, you can also use this syntax in list, dict, tuple, and set displays (also sometimes called literals). See PEP 488: Additional Unpacking Generalizations.

>>> (0, *range(1, 4), 5, *range(6, 8))
(0, 1, 2, 3, 5, 6, 7)
>>> [0, *range(1, 4), 5, *range(6, 8)]
[0, 1, 2, 3, 5, 6, 7]
>>> {0, *range(1, 4), 5, *range(6, 8)}
{0, 1, 2, 3, 5, 6, 7}
>>> d = {'one': 1, 'two': 2, 'three': 3}
>>> e = {'six': 6, 'seven': 7}
>>> {'zero': 0, **d, 'five': 5, **e}
{'five': 5, 'seven': 7, 'two': 2, 'one': 1, 'three': 3, 'six': 6, 'zero': 0}

It also allows multiple iterables to be unpacked in a single function call.

>>> range(*[1, 10], *[2])
range(1, 10, 2)

(Thanks to mgilson for the PEP link.)


回答 12

除函数调用外,* args和** kwargs在类层次结构中很有用,并且还避免了必须__init__在Python中编写方法。在类似Django代码的框架中可以看到类似的用法。

例如,

def __init__(self, *args, **kwargs):
    for attribute_name, value in zip(self._expected_attributes, args):
        setattr(self, attribute_name, value)
        if kwargs.has_key(attribute_name):
            kwargs.pop(attribute_name)

    for attribute_name in kwargs.viewkeys():
        setattr(self, attribute_name, kwargs[attribute_name])

子类可以是

class RetailItem(Item):
    _expected_attributes = Item._expected_attributes + ['name', 'price', 'category', 'country_of_origin']

class FoodItem(RetailItem):
    _expected_attributes = RetailItem._expected_attributes +  ['expiry_date']

然后将该子类实例化为

food_item = FoodItem(name = 'Jam', 
                     price = 12.0, 
                     category = 'Foods', 
                     country_of_origin = 'US', 
                     expiry_date = datetime.datetime.now())

此外,具有仅对该子类实例有意义的新属性的子类可以调用Base类__init__以卸载属性设置。这是通过* args和** kwargs完成的。主要使用kwargs,以便使用命名参数可以读取代码。例如,

class ElectronicAccessories(RetailItem):
    _expected_attributes = RetailItem._expected_attributes +  ['specifications']
    # Depend on args and kwargs to populate the data as needed.
    def __init__(self, specifications = None, *args, **kwargs):
        self.specifications = specifications  # Rest of attributes will make sense to parent class.
        super(ElectronicAccessories, self).__init__(*args, **kwargs)

可以被形容为

usb_key = ElectronicAccessories(name = 'Sandisk', 
                                price = '$6.00', 
                                category = 'Electronics',
                                country_of_origin = 'CN',
                                specifications = '4GB USB 2.0/USB 3.0')

完整的代码在这里

In addition to function calls, *args and **kwargs are useful in class hierarchies and also avoid having to write __init__ method in Python. Similar usage can seen in frameworks like Django code.

For example,

def __init__(self, *args, **kwargs):
    for attribute_name, value in zip(self._expected_attributes, args):
        setattr(self, attribute_name, value)
        if kwargs.has_key(attribute_name):
            kwargs.pop(attribute_name)

    for attribute_name in kwargs.viewkeys():
        setattr(self, attribute_name, kwargs[attribute_name])

A subclass can then be

class RetailItem(Item):
    _expected_attributes = Item._expected_attributes + ['name', 'price', 'category', 'country_of_origin']

class FoodItem(RetailItem):
    _expected_attributes = RetailItem._expected_attributes +  ['expiry_date']

The subclass then be instantiated as

food_item = FoodItem(name = 'Jam', 
                     price = 12.0, 
                     category = 'Foods', 
                     country_of_origin = 'US', 
                     expiry_date = datetime.datetime.now())

Also, a subclass with a new attribute which makes sense only to that subclass instance can call the Base class __init__ to offload the attributes setting. This is done through *args and **kwargs. kwargs mainly used so that code is readable using named arguments. For example,

class ElectronicAccessories(RetailItem):
    _expected_attributes = RetailItem._expected_attributes +  ['specifications']
    # Depend on args and kwargs to populate the data as needed.
    def __init__(self, specifications = None, *args, **kwargs):
        self.specifications = specifications  # Rest of attributes will make sense to parent class.
        super(ElectronicAccessories, self).__init__(*args, **kwargs)

which can be instatiated as

usb_key = ElectronicAccessories(name = 'Sandisk', 
                                price = '$6.00', 
                                category = 'Electronics',
                                country_of_origin = 'CN',
                                specifications = '4GB USB 2.0/USB 3.0')

The complete code is here


回答 13

建立在昵称的答案上

def foo(param1, *param2):
    print(param1)
    print(param2)


def bar(param1, **param2):
    print(param1)
    print(param2)


def three_params(param1, *param2, **param3):
    print(param1)
    print(param2)
    print(param3)


foo(1, 2, 3, 4, 5)
print("\n")
bar(1, a=2, b=3)
print("\n")
three_params(1, 2, 3, 4, s=5)

输出:

1
(2, 3, 4, 5)

1
{'a': 2, 'b': 3}

1
(2, 3, 4)
{'s': 5}

基本上,任何数量的位置参数都可以使用* args,任何命名参数(或kwargs aka关键字参数)都可以使用** kwargs。

Building on nickd’s answer

def foo(param1, *param2):
    print(param1)
    print(param2)


def bar(param1, **param2):
    print(param1)
    print(param2)


def three_params(param1, *param2, **param3):
    print(param1)
    print(param2)
    print(param3)


foo(1, 2, 3, 4, 5)
print("\n")
bar(1, a=2, b=3)
print("\n")
three_params(1, 2, 3, 4, s=5)

Output:

1
(2, 3, 4, 5)

1
{'a': 2, 'b': 3}

1
(2, 3, 4)
{'s': 5}

Basically, any number of positional arguments can use *args and any named arguments (or kwargs aka keyword arguments) can use **kwargs.


回答 14

*args**kwargs:允许您将可变数量的参数传递给函数。

*args:用于将非关键字的可变长度参数列表发送给函数:

def args(normal_arg, *argv):
    print("normal argument:", normal_arg)

    for arg in argv:
        print("Argument in list of arguments from *argv:", arg)

args('animals', 'fish', 'duck', 'bird')

将生成:

normal argument: animals
Argument in list of arguments from *argv: fish
Argument in list of arguments from *argv: duck
Argument in list of arguments from *argv: bird

**kwargs*

**kwargs允许您将关键字的可变参数长度传递给函数。**kwargs如果要处理函数中的命名参数,则应使用。

def who(**kwargs):
    if kwargs is not None:
        for key, value in kwargs.items():
            print("Your %s is %s." % (key, value))

who(name="Nikola", last_name="Tesla", birthday="7.10.1856", birthplace="Croatia")  

将生成:

Your name is Nikola.
Your last_name is Tesla.
Your birthday is 7.10.1856.
Your birthplace is Croatia.

*args and **kwargs: allow you to pass a variable number of arguments to a function.

*args: is used to send a non-keyworded variable length argument list to the function:

def args(normal_arg, *argv):
    print("normal argument:", normal_arg)

    for arg in argv:
        print("Argument in list of arguments from *argv:", arg)

args('animals', 'fish', 'duck', 'bird')

Will produce:

normal argument: animals
Argument in list of arguments from *argv: fish
Argument in list of arguments from *argv: duck
Argument in list of arguments from *argv: bird

**kwargs*

**kwargs allows you to pass keyworded variable length of arguments to a function. You should use **kwargs if you want to handle named arguments in a function.

def who(**kwargs):
    if kwargs is not None:
        for key, value in kwargs.items():
            print("Your %s is %s." % (key, value))

who(name="Nikola", last_name="Tesla", birthday="7.10.1856", birthplace="Croatia")  

Will produce:

Your name is Nikola.
Your last_name is Tesla.
Your birthday is 7.10.1856.
Your birthplace is Croatia.

回答 15

这个例子可以帮助您记住*args**kwargs甚至super可以立即在Python中继承。

class base(object):
    def __init__(self, base_param):
        self.base_param = base_param


class child1(base): # inherited from base class
    def __init__(self, child_param, *args) # *args for non-keyword args
        self.child_param = child_param
        super(child1, self).__init__(*args) # call __init__ of the base class and initialize it with a NON-KEYWORD arg

class child2(base):
    def __init__(self, child_param, **kwargs):
        self.child_param = child_param
        super(child2, self).__init__(**kwargs) # call __init__ of the base class and initialize it with a KEYWORD arg

c1 = child1(1,0)
c2 = child2(1,base_param=0)
print c1.base_param # 0
print c1.child_param # 1
print c2.base_param # 0
print c2.child_param # 1

This example would help you remember *args, **kwargs and even super and inheritance in Python at once.

class base(object):
    def __init__(self, base_param):
        self.base_param = base_param


class child1(base): # inherited from base class
    def __init__(self, child_param, *args) # *args for non-keyword args
        self.child_param = child_param
        super(child1, self).__init__(*args) # call __init__ of the base class and initialize it with a NON-KEYWORD arg

class child2(base):
    def __init__(self, child_param, **kwargs):
        self.child_param = child_param
        super(child2, self).__init__(**kwargs) # call __init__ of the base class and initialize it with a KEYWORD arg

c1 = child1(1,0)
c2 = child2(1,base_param=0)
print c1.base_param # 0
print c1.child_param # 1
print c2.base_param # 0
print c2.child_param # 1

回答 16

在函数中同时使用两者的一个很好的例子是:

>>> def foo(*arg,**kwargs):
...     print arg
...     print kwargs
>>>
>>> a = (1, 2, 3)
>>> b = {'aa': 11, 'bb': 22}
>>>
>>>
>>> foo(*a,**b)
(1, 2, 3)
{'aa': 11, 'bb': 22}
>>>
>>>
>>> foo(a,**b) 
((1, 2, 3),)
{'aa': 11, 'bb': 22}
>>>
>>>
>>> foo(a,b) 
((1, 2, 3), {'aa': 11, 'bb': 22})
{}
>>>
>>>
>>> foo(a,*b)
((1, 2, 3), 'aa', 'bb')
{}

A good example of using both in a function is:

>>> def foo(*arg,**kwargs):
...     print arg
...     print kwargs
>>>
>>> a = (1, 2, 3)
>>> b = {'aa': 11, 'bb': 22}
>>>
>>>
>>> foo(*a,**b)
(1, 2, 3)
{'aa': 11, 'bb': 22}
>>>
>>>
>>> foo(a,**b) 
((1, 2, 3),)
{'aa': 11, 'bb': 22}
>>>
>>>
>>> foo(a,b) 
((1, 2, 3), {'aa': 11, 'bb': 22})
{}
>>>
>>>
>>> foo(a,*b)
((1, 2, 3), 'aa', 'bb')
{}

回答 17

TL; DR

以下是6种不同的使用情况*,并**在Python编程:

  1. 要使用*args:接受任意数量的位置参数 def foo(*args): pass,此处foo接受任意数量的位置参数,即以下调用有效foo(1)foo(1, 'bar')
  2. 要使用**kwargs:接受任意数量的关键字参数 def foo(**kwargs): pass,此处的’foo’接受任意数量的关键字参数,即以下调用有效foo(name='Tom')foo(name='Tom', age=33)
  3. 要使用*args, **kwargs:接受任意数量的位置和关键字参数 def foo(*args, **kwargs): pass,此处foo接受任意数量的位置和关键字参数,即以下调用是有效的foo(1,name='Tom')foo(1, 'bar', name='Tom', age=33)
  4. 要使用*:强制使用 仅关键字参数def foo(pos1, pos2, *, kwarg1): pass,这*意味着foo仅在pos2之后接受关键字参数,因此foo(1, 2, 3)引发TypeError但foo(1, 2, kwarg1=3)可以。
  5. 要使用*_(注:仅是一种约定)对更多的位置参数不再表示兴趣 def foo(bar, baz, *_): pass:(按约定)意味着(按约定)在其工作中foo仅使用barbaz参数,而将忽略其他参数。
  6. 要使用\**_(注:仅是一种约定)对更多关键字参数不再表示兴趣 def foo(bar, baz, **_): pass:(按约定)意味着(按约定)在其工作中foo仅使用barbaz参数,而将忽略其他参数。

奖励:从python 3.8开始,可以/在函数定义中使用来强制仅位置参数。在以下示例中,参数a和b是仅位置信息,而c或d可以是位置信息或关键字,而e或f必须是关键字:

def f(a, b, /, c, d, *, e, f):
    pass

TL;DR

Below are 6 different use cases for * and ** in python programming:

  1. To accept any number of positional arguments using *args: def foo(*args): pass, here foo accepts any number of positional arguments, i. e., the following calls are valid foo(1), foo(1, 'bar')
  2. To accept any number of keyword arguments using **kwargs: def foo(**kwargs): pass, here ‘foo’ accepts any number of keyword arguments, i. e., the following calls are valid foo(name='Tom'), foo(name='Tom', age=33)
  3. To accept any number of positional and keyword arguments using *args, **kwargs: def foo(*args, **kwargs): pass, here foo accepts any number of positional and keyword arguments, i. e., the following calls are valid foo(1,name='Tom'), foo(1, 'bar', name='Tom', age=33)
  4. To enforce keyword only arguments using *: def foo(pos1, pos2, *, kwarg1): pass, here * means that foo only accept keyword arguments after pos2, hence foo(1, 2, 3) raises TypeError but foo(1, 2, kwarg1=3) is ok.
  5. To express no further interest in more positional arguments using *_ (Note: this is a convention only): def foo(bar, baz, *_): pass means (by convention) foo only uses bar and baz arguments in its working and will ignore others.
  6. To express no further interest in more keyword arguments using \**_ (Note: this is a convention only): def foo(bar, baz, **_): pass means (by convention) foo only uses bar and baz arguments in its working and will ignore others.

BONUS: From python 3.8 onward, one can use / in function definition to enforce positional only parameters. In the following example, parameters a and b are positional-only, while c or d can be positional or keyword, and e or f are required to be keywords:

def f(a, b, /, c, d, *, e, f):
    pass

回答 18

TL; DR

它包传递给函数的参数将listdict分别在函数体中。当您定义函数签名时,如下所示:

def func(*args, **kwds):
    # do stuff

可以使用任意数量的参数和关键字参数来调用它。非关键字参数打包到args函数体内调用的列表中,而关键字参数打包到kwds函数体内调用的dict中。

func("this", "is a list of", "non-keyowrd", "arguments", keyword="ligma", options=[1,2,3])

现在函数体,当函数被调用里面,有两个局部变量,args这是一个有值列表["this", "is a list of", "non-keyword", "arguments"]kwds它是一个dict具有价值{"keyword" : "ligma", "options" : [1,2,3]}


这也可以反向进行,即从呼叫方进行。例如,如果您将函数定义为:

def f(a, b, c, d=1, e=10):
    # do stuff

您可以通过解压缩调用范围中的迭代器或映射来调用它:

iterable = [1, 20, 500]
mapping = {"d" : 100, "e": 3}
f(*iterable, **mapping)
# That call is equivalent to
f(1, 20, 500, d=100, e=3)

TL;DR

It packs arguments passed to the function into list and dict respectively inside the function body. When you define a function signature like this:

def func(*args, **kwds):
    # do stuff

it can be called with any number of arguments and keyword arguments. The non-keyword arguments get packed into a list called args inside the the function body and the keyword arguments get packed into a dict called kwds inside the function body.

func("this", "is a list of", "non-keyowrd", "arguments", keyword="ligma", options=[1,2,3])

now inside the function body, when the function is called, there are two local variables, args which is a list having value ["this", "is a list of", "non-keyword", "arguments"] and kwds which is a dict having value {"keyword" : "ligma", "options" : [1,2,3]}


This also works in reverse, i.e. from the caller side. for example if you have a function defined as:

def f(a, b, c, d=1, e=10):
    # do stuff

you can call it with by unpacking iterables or mappings you have in the calling scope:

iterable = [1, 20, 500]
mapping = {"d" : 100, "e": 3}
f(*iterable, **mapping)
# That call is equivalent to
f(1, 20, 500, d=100, e=3)

回答 19

语境

  • python 3.x
  • 开箱 **
  • 与字符串格式一起使用

与字符串格式一起使用

除了此主题中的答案外,这是其他地方未提及的另一个细节。这扩展了布拉德·所罗门答案

**使用python时,使用进行解包也很有用str.format

这有点类似于您可以使用python f-strings f-string进行的操作,但是增加了声明保留变量的字典的开销(f-string不需要字典)。

快速范例

  ## init vars
  ddvars = dict()
  ddcalc = dict()
  pass
  ddvars['fname']     = 'Huomer'
  ddvars['lname']     = 'Huimpson'
  ddvars['motto']     = 'I love donuts!'
  ddvars['age']       = 33
  pass
  ddcalc['ydiff']     = 5
  ddcalc['ycalc']     = ddvars['age'] + ddcalc['ydiff']
  pass
  vdemo = []

  ## ********************
  ## single unpack supported in py 2.7
  vdemo.append('''
  Hello {fname} {lname}!

  Today you are {age} years old!

  We love your motto "{motto}" and we agree with you!
  '''.format(**ddvars)) 
  pass

  ## ********************
  ## multiple unpack supported in py 3.x
  vdemo.append('''
  Hello {fname} {lname}!

  In {ydiff} years you will be {ycalc} years old!
  '''.format(**ddvars,**ddcalc)) 
  pass

  ## ********************
  print(vdemo[-1])

Context

  • python 3.x
  • unpacking with **
  • use with string formatting

Use with string formatting

In addition to the answers in this thread, here is another detail that was not mentioned elsewhere. This expands on the answer by Brad Solomon

Unpacking with ** is also useful when using python str.format.

This is somewhat similar to what you can do with python f-strings f-string but with the added overhead of declaring a dict to hold the variables (f-string does not require a dict).

Quick Example

  ## init vars
  ddvars = dict()
  ddcalc = dict()
  pass
  ddvars['fname']     = 'Huomer'
  ddvars['lname']     = 'Huimpson'
  ddvars['motto']     = 'I love donuts!'
  ddvars['age']       = 33
  pass
  ddcalc['ydiff']     = 5
  ddcalc['ycalc']     = ddvars['age'] + ddcalc['ydiff']
  pass
  vdemo = []

  ## ********************
  ## single unpack supported in py 2.7
  vdemo.append('''
  Hello {fname} {lname}!

  Today you are {age} years old!

  We love your motto "{motto}" and we agree with you!
  '''.format(**ddvars)) 
  pass

  ## ********************
  ## multiple unpack supported in py 3.x
  vdemo.append('''
  Hello {fname} {lname}!

  In {ydiff} years you will be {ycalc} years old!
  '''.format(**ddvars,**ddcalc)) 
  pass

  ## ********************
  print(vdemo[-1])


回答 20

  • def foo(param1, *param2):是一种方法,可以接受任意数量的值*param2
  • def bar(param1, **param2): 是一种可以使用键接受任意数量的值的方法 *param2
  • param1 是一个简单的参数。

例如,在Java中实现varargs的语法如下:

accessModifier methodName(datatype arg) {
    // method body
}
  • def foo(param1, *param2): is a method can accept arbitrary number of values for *param2,
  • def bar(param1, **param2): is a method can accept arbitrary number of values with keys for *param2
  • param1 is a simple parameter.

For example, the syntax for implementing varargs in Java as follows:

accessModifier methodName(datatype… arg) {
    // method body
}

如何通过引用传递变量?

问题:如何通过引用传递变量?

Python文档似乎尚不清楚参数是通过引用还是通过值传递,并且以下代码产生不变的值“原始”

class PassByReference:
    def __init__(self):
        self.variable = 'Original'
        self.change(self.variable)
        print(self.variable)

    def change(self, var):
        var = 'Changed'

我可以做些什么来通过实际引用传递变量吗?

The Python documentation seems unclear about whether parameters are passed by reference or value, and the following code produces the unchanged value ‘Original’

class PassByReference:
    def __init__(self):
        self.variable = 'Original'
        self.change(self.variable)
        print(self.variable)

    def change(self, var):
        var = 'Changed'

Is there something I can do to pass the variable by actual reference?


回答 0

参数通过赋值传递。其背后的理由是双重的:

  1. 传入的参数实际上是对象的引用(但引用是按值传递的)
  2. 有些数据类型是可变的,但有些不是

所以:

  • 如果将可变对象传递给方法,则该方法将获得对该对象的引用,并且可以对其进行突变,但是如果您将该引用重新绑定到该方法中,则外部作用域对此一无所知完成后,外部参考仍将指向原始对象。

  • 如果将不可变对象传递给方法,则仍然无法重新绑定外部引用,甚至无法使对象发生突变。

为了更加清楚,让我们举一些例子。

列表-可变类型

让我们尝试修改传递给方法的列表:

def try_to_change_list_contents(the_list):
    print('got', the_list)
    the_list.append('four')
    print('changed to', the_list)

outer_list = ['one', 'two', 'three']

print('before, outer_list =', outer_list)
try_to_change_list_contents(outer_list)
print('after, outer_list =', outer_list)

输出:

before, outer_list = ['one', 'two', 'three']
got ['one', 'two', 'three']
changed to ['one', 'two', 'three', 'four']
after, outer_list = ['one', 'two', 'three', 'four']

由于传入的参数是对的引用outer_list,而不是其副本,因此我们可以使用变异列表方法对其进行更改,并使更改反映在外部作用域中。

现在,让我们看看当尝试更改作为参数传入的引用时会发生什么:

def try_to_change_list_reference(the_list):
    print('got', the_list)
    the_list = ['and', 'we', 'can', 'not', 'lie']
    print('set to', the_list)

outer_list = ['we', 'like', 'proper', 'English']

print('before, outer_list =', outer_list)
try_to_change_list_reference(outer_list)
print('after, outer_list =', outer_list)

输出:

before, outer_list = ['we', 'like', 'proper', 'English']
got ['we', 'like', 'proper', 'English']
set to ['and', 'we', 'can', 'not', 'lie']
after, outer_list = ['we', 'like', 'proper', 'English']

由于the_list参数是通过值传递的,因此为其分配新列表不会影响方法外部的代码。该the_list是副本outer_list的参考,我们不得不the_list指向一个新的列表,但没有办法改变,其中outer_list尖。

字符串-不可变的类型

它是不可变的,因此我们无能为力,无法更改字符串的内容

现在,让我们尝试更改参考

def try_to_change_string_reference(the_string):
    print('got', the_string)
    the_string = 'In a kingdom by the sea'
    print('set to', the_string)

outer_string = 'It was many and many a year ago'

print('before, outer_string =', outer_string)
try_to_change_string_reference(outer_string)
print('after, outer_string =', outer_string)

输出:

before, outer_string = It was many and many a year ago
got It was many and many a year ago
set to In a kingdom by the sea
after, outer_string = It was many and many a year ago

同样,由于the_string参数是通过值传递的,因此为其分配新的字符串不会影响方法外部的代码。该the_string是副本outer_string的参考,我们不得不the_string指向一个新的字符串,但没有办法改变,其中outer_string尖。

我希望这可以使事情变得简单。

编辑:注意到这并不能回答@David最初询问的问题,“我可以做些什么来通过实际引用传递变量吗?”。让我们继续努力。

我们如何解决这个问题?

如@Andrea的答案所示,您可以返回新值。这不会改变事物传递的方式,但是可以让您获取想要的信息:

def return_a_whole_new_string(the_string):
    new_string = something_to_do_with_the_old_string(the_string)
    return new_string

# then you could call it like
my_string = return_a_whole_new_string(my_string)

如果您确实想避免使用返回值,则可以创建一个类来保存您的值,并将其传递给函数或使用现有的类,例如列表:

def use_a_wrapper_to_simulate_pass_by_reference(stuff_to_change):
    new_string = something_to_do_with_the_old_string(stuff_to_change[0])
    stuff_to_change[0] = new_string

# then you could call it like
wrapper = [my_string]
use_a_wrapper_to_simulate_pass_by_reference(wrapper)

do_something_with(wrapper[0])

尽管这看起来有点麻烦。

Arguments are passed by assignment. The rationale behind this is twofold:

  1. the parameter passed in is actually a reference to an object (but the reference is passed by value)
  2. some data types are mutable, but others aren’t

So:

  • If you pass a mutable object into a method, the method gets a reference to that same object and you can mutate it to your heart’s delight, but if you rebind the reference in the method, the outer scope will know nothing about it, and after you’re done, the outer reference will still point at the original object.

  • If you pass an immutable object to a method, you still can’t rebind the outer reference, and you can’t even mutate the object.

To make it even more clear, let’s have some examples.

List – a mutable type

Let’s try to modify the list that was passed to a method:

def try_to_change_list_contents(the_list):
    print('got', the_list)
    the_list.append('four')
    print('changed to', the_list)

outer_list = ['one', 'two', 'three']

print('before, outer_list =', outer_list)
try_to_change_list_contents(outer_list)
print('after, outer_list =', outer_list)

Output:

before, outer_list = ['one', 'two', 'three']
got ['one', 'two', 'three']
changed to ['one', 'two', 'three', 'four']
after, outer_list = ['one', 'two', 'three', 'four']

Since the parameter passed in is a reference to outer_list, not a copy of it, we can use the mutating list methods to change it and have the changes reflected in the outer scope.

Now let’s see what happens when we try to change the reference that was passed in as a parameter:

def try_to_change_list_reference(the_list):
    print('got', the_list)
    the_list = ['and', 'we', 'can', 'not', 'lie']
    print('set to', the_list)

outer_list = ['we', 'like', 'proper', 'English']

print('before, outer_list =', outer_list)
try_to_change_list_reference(outer_list)
print('after, outer_list =', outer_list)

Output:

before, outer_list = ['we', 'like', 'proper', 'English']
got ['we', 'like', 'proper', 'English']
set to ['and', 'we', 'can', 'not', 'lie']
after, outer_list = ['we', 'like', 'proper', 'English']

Since the the_list parameter was passed by value, assigning a new list to it had no effect that the code outside the method could see. The the_list was a copy of the outer_list reference, and we had the_list point to a new list, but there was no way to change where outer_list pointed.

String – an immutable type

It’s immutable, so there’s nothing we can do to change the contents of the string

Now, let’s try to change the reference

def try_to_change_string_reference(the_string):
    print('got', the_string)
    the_string = 'In a kingdom by the sea'
    print('set to', the_string)

outer_string = 'It was many and many a year ago'

print('before, outer_string =', outer_string)
try_to_change_string_reference(outer_string)
print('after, outer_string =', outer_string)

Output:

before, outer_string = It was many and many a year ago
got It was many and many a year ago
set to In a kingdom by the sea
after, outer_string = It was many and many a year ago

Again, since the the_string parameter was passed by value, assigning a new string to it had no effect that the code outside the method could see. The the_string was a copy of the outer_string reference, and we had the_string point to a new string, but there was no way to change where outer_string pointed.

I hope this clears things up a little.

EDIT: It’s been noted that this doesn’t answer the question that @David originally asked, “Is there something I can do to pass the variable by actual reference?”. Let’s work on that.

How do we get around this?

As @Andrea’s answer shows, you could return the new value. This doesn’t change the way things are passed in, but does let you get the information you want back out:

def return_a_whole_new_string(the_string):
    new_string = something_to_do_with_the_old_string(the_string)
    return new_string

# then you could call it like
my_string = return_a_whole_new_string(my_string)

If you really wanted to avoid using a return value, you could create a class to hold your value and pass it into the function or use an existing class, like a list:

def use_a_wrapper_to_simulate_pass_by_reference(stuff_to_change):
    new_string = something_to_do_with_the_old_string(stuff_to_change[0])
    stuff_to_change[0] = new_string

# then you could call it like
wrapper = [my_string]
use_a_wrapper_to_simulate_pass_by_reference(wrapper)

do_something_with(wrapper[0])

Although this seems a little cumbersome.


回答 1

问题来自对Python中的变量有误解。如果您习惯了大多数传统语言,那么您将对以下顺序有一个心理模型:

a = 1
a = 2

您认为这a是存储值的存储位置1,然后将其更新以存储值2。这不是Python中的工作方式。相反,a首先将其作为对具有值的对象的引用1,然后将其重新分配为对具有value的对象的引用2。即使a不再引用第一个对象,这两个对象也可能继续存在。实际上,它们可以被程序中的许多其他引用共享。

当您使用参数调用函数时,将创建一个新引用,该引用引用传入的对象。这与函数调用中使用的引用是分开的,因此无法更新该引用并将其引用为新对象。在您的示例中:

def __init__(self):
    self.variable = 'Original'
    self.Change(self.variable)

def Change(self, var):
    var = 'Changed'

self.variable是对字符串对象的引用'Original'。调用时,Change您将创建var对该对象的第二个引用。在函数内部,您将引用重新分配var给另一个字符串对象'Changed',但是引用self.variable是独立的,并且不会更改。

解决此问题的唯一方法是传递一个可变对象。因为两个引用都引用同一个对象,所以对对象的任何更改都会在两个地方反映出来。

def __init__(self):         
    self.variable = ['Original']
    self.Change(self.variable)

def Change(self, var):
    var[0] = 'Changed'

The problem comes from a misunderstanding of what variables are in Python. If you’re used to most traditional languages, you have a mental model of what happens in the following sequence:

a = 1
a = 2

You believe that a is a memory location that stores the value 1, then is updated to store the value 2. That’s not how things work in Python. Rather, a starts as a reference to an object with the value 1, then gets reassigned as a reference to an object with the value 2. Those two objects may continue to coexist even though a doesn’t refer to the first one anymore; in fact they may be shared by any number of other references within the program.

When you call a function with a parameter, a new reference is created that refers to the object passed in. This is separate from the reference that was used in the function call, so there’s no way to update that reference and make it refer to a new object. In your example:

def __init__(self):
    self.variable = 'Original'
    self.Change(self.variable)

def Change(self, var):
    var = 'Changed'

self.variable is a reference to the string object 'Original'. When you call Change you create a second reference var to the object. Inside the function you reassign the reference var to a different string object 'Changed', but the reference self.variable is separate and does not change.

The only way around this is to pass a mutable object. Because both references refer to the same object, any changes to the object are reflected in both places.

def __init__(self):         
    self.variable = ['Original']
    self.Change(self.variable)

def Change(self, var):
    var[0] = 'Changed'

回答 2

我发现其他答案相当冗长和复杂,因此我创建了这个简单的图来解释Python处理变量和参数的方式。

I found the other answers rather long and complicated, so I created this simple diagram to explain the way Python treats variables and parameters.


回答 3

它既不是值传递也不是引用传递-它是对象调用。参见Fredrik Lundh的观点:

http://effbot.org/zone/call-by-object.htm

这是一个重要的报价:

“ …变量[名称] 不是对象;它们不能由其他变量表示或由对象引用。”

在您的示例中,Change调用该方法时-为该方法创建了一个命名空间;并var在该命名空间中成为字符串object的名称'Original'。然后,该对象在两个命名空间中都有一个名称。接下来,var = 'Changed'绑定var到新的字符串对象,因此该方法的命名空间会忘记'Original'。最后,忘记了该命名空间,以及字符串'Changed'

It is neither pass-by-value or pass-by-reference – it is call-by-object. See this, by Fredrik Lundh:

http://effbot.org/zone/call-by-object.htm

Here is a significant quote:

“…variables [names] are not objects; they cannot be denoted by other variables or referred to by objects.”

In your example, when the Change method is called–a namespace is created for it; and var becomes a name, within that namespace, for the string object 'Original'. That object then has a name in two namespaces. Next, var = 'Changed' binds var to a new string object, and thus the method’s namespace forgets about 'Original'. Finally, that namespace is forgotten, and the string 'Changed' along with it.


回答 4

考虑一下通过分配而不是通过引用/按值传递的内容。这样,就很清楚,只要您了解正常分配过程中发生了什么,就会发生什么情况。

因此,当将列表传递给函数/方法时,该列表将分配给参数名称。追加到列表将导致列表被修改。在函数重新分配列表不会更改原始列表,因为:

a = [1, 2, 3]
b = a
b.append(4)
b = ['a', 'b']
print a, b      # prints [1, 2, 3, 4] ['a', 'b']

由于不可变类型无法修改,因此它们看起来像是通过值传递-将int传递给函数意味着将int分配给函数的参数。您只能重新分配它,但不会更改原始变量的值。

Think of stuff being passed by assignment instead of by reference/by value. That way, it is always clear, what is happening as long as you understand what happens during the normal assignment.

So, when passing a list to a function/method, the list is assigned to the parameter name. Appending to the list will result in the list being modified. Reassigning the list inside the function will not change the original list, since:

a = [1, 2, 3]
b = a
b.append(4)
b = ['a', 'b']
print a, b      # prints [1, 2, 3, 4] ['a', 'b']

Since immutable types cannot be modified, they seem like being passed by value – passing an int into a function means assigning the int to the function’s parameter. You can only ever reassign that, but it won’t change the original variables value.


回答 5

Effbot(又名Fredrik Lundh)将Python的变量传递样式描述为按对象调用:http : //effbot.org/zone/call-by-object.htm

对象在堆上分配,指向它们的指针可以在任何地方传递。

  • 当您进行诸如的分配时x = 1000,将创建一个字典条目,该条目将当前命名空间中的字符串“ x”映射到指向包含一千的整数对象的指针。

  • 当您使用来更新“ x”时x = 2000,将创建一个新的整数对象,并且字典将更新为指向该新对象。旧的一千个对象保持不变(取决于其他是否引用了该对象,该对象是否可以存活)。

  • 当您进行诸如的新分配时y = x,将创建一个新的字典条目“ y”,该条目指向与“ x”条目相同的对象。

  • 诸如字符串和整数之类的对象是不可变的。这仅表示没有任何方法可以在创建对象后更改该对象。例如,一旦创建了整数对象1000,它就永远不会改变。数学是通过创建新的整数对象完成的。

  • 像列表这样的对象是可变的。这意味着可以通过指向该对象的任何内容来更改该对象的内容。例如,x = []; y = x; x.append(10); print y将打印[10]。空列表已创建。“ x”和“ y”都指向同一列表。该追加方法变异(更新)列表中的对象(如添加一条记录到数据库),其结果是可见的两个“X”和“Y”(就像一个数据库更新将每一个连接到该数据库中可见)。

希望能为您解决问题。

Effbot (aka Fredrik Lundh) has described Python’s variable passing style as call-by-object: http://effbot.org/zone/call-by-object.htm

Objects are allocated on the heap and pointers to them can be passed around anywhere.

  • When you make an assignment such as x = 1000, a dictionary entry is created that maps the string “x” in the current namespace to a pointer to the integer object containing one thousand.

  • When you update “x” with x = 2000, a new integer object is created and the dictionary is updated to point at the new object. The old one thousand object is unchanged (and may or may not be alive depending on whether anything else refers to the object).

  • When you do a new assignment such as y = x, a new dictionary entry “y” is created that points to the same object as the entry for “x”.

  • Objects like strings and integers are immutable. This simply means that there are no methods that can change the object after it has been created. For example, once the integer object one-thousand is created, it will never change. Math is done by creating new integer objects.

  • Objects like lists are mutable. This means that the contents of the object can be changed by anything pointing to the object. For example, x = []; y = x; x.append(10); print y will print [10]. The empty list was created. Both “x” and “y” point to the same list. The append method mutates (updates) the list object (like adding a record to a database) and the result is visible to both “x” and “y” (just as a database update would be visible to every connection to that database).

Hope that clarifies the issue for you.


回答 6

从技术上讲,Python始终使用按引用传递值。我将重复其他答案以支持我的发言。

Python始终使用按引用传递值。也不exceptions。任何变量分配都意味着复制参考值。没有exceptions。任何变量都是绑定到参考值的名称。总是。

您可以将参考值视为目标对象的地址。该地址在使用时会自动取消引用。这样,使用参考值,似乎您可以直接使用目标对象。但是,两者之间总是存在一个参考,距离目标还有一步之遥。

这是证明Python使用引用传递的示例:

如果参数通过值传递,则外部lst不能被修改。绿色是目标对象(黑色是内部存储的值,红色是对象类型),黄色是内部具有参考值的存储器-绘制为箭头。蓝色实心箭头是传递给函数的参考值(通过虚线蓝色箭头路径)。丑陋的深黄色是内部词典。(实际上也可以将其绘制为绿色椭圆形。颜色和形状仅表示它是内部的。)

您可以使用id()内置函数来了解参考值是什么(即目标对象的地址)。

在编译语言中,变量是能够捕获类型值的内存空间。在Python中,变量是绑定到引用变量的名称(在内部以字符串形式捕获),该变量将引用值保存到目标对象。变量的名称是内部字典中的键,该字典项的值部分将参考值存储到目标。

参考值隐藏在Python中。没有用于存储参考值的任何明确的用户类型。但是,您可以将list元素(或任何其他合适的容器类型的元素)用作引用变量,因为所有容器都确实将元素存储为对目标对象的引用。换句话说,元素实际上不包含在容器内,仅包含对元素的引用。

Technically, Python always uses pass by reference values. I am going to repeat my other answer to support my statement.

Python always uses pass-by-reference values. There isn’t any exception. Any variable assignment means copying the reference value. No exception. Any variable is the name bound to the reference value. Always.

You can think about a reference value as the address of the target object. The address is automatically dereferenced when used. This way, working with the reference value, it seems you work directly with the target object. But there always is a reference in between, one step more to jump to the target.

Here is the example that proves that Python uses passing by reference:

If the argument was passed by value, the outer lst could not be modified. The green are the target objects (the black is the value stored inside, the red is the object type), the yellow is the memory with the reference value inside — drawn as the arrow. The blue solid arrow is the reference value that was passed to the function (via the dashed blue arrow path). The ugly dark yellow is the internal dictionary. (It actually could be drawn also as a green ellipse. The colour and the shape only says it is internal.)

You can use the id() built-in function to learn what the reference value is (that is, the address of the target object).

In compiled languages, a variable is a memory space that is able to capture the value of the type. In Python, a variable is a name (captured internally as a string) bound to the reference variable that holds the reference value to the target object. The name of the variable is the key in the internal dictionary, the value part of that dictionary item stores the reference value to the target.

Reference values are hidden in Python. There isn’t any explicit user type for storing the reference value. However, you can use a list element (or element in any other suitable container type) as the reference variable, because all containers do store the elements also as references to the target objects. In other words, elements are actually not contained inside the container — only the references to elements are.


回答 7

Python中没有变量

理解参数传递的关键是停止考虑“变量”。Python中有名称和对象,它们一起看起来像变量,但是始终区分这三个名称很有用。

  1. Python具有名称和对象。
  2. 分配将名称绑定到对象。
  3. 将参数传递给函数还会将名称(函数的参数名称)绑定到对象。

这就是全部。可变性与这个问题无关。

例:

a = 1

这会将名称绑定a到具有值1的整数类型的对象。

b = x

这会将名称绑定到b该名称x当前绑定到的同一对象。之后,该b名称x不再与该名称相关。

请参阅Python 3语言参考中的3.14.2节。

如何阅读问题中的例子

在问题所示的代码中,该语句self.Change(self.variable)将名称var(在function的范围内Change)绑定到保存该值的对象,'Original'而赋值var = 'Changed'(在function的主体中Change)再次将同一个名称分配给其他对象(发生这种情况)也可以容纳一个字符串,但完全可以是其他东西)。

如何通过参考

因此,如果您要更改的对象是可变对象,则没有问题,因为所有内容均有效地通过引用传递。

如果它是一个不可变的对象(例如布尔,数字,字符串),则将其包装在一个可变的对象中。
对此的快速解决方案是一个元素列表(而不是self.variable,pass [self.variable]和函数Modify var[0])。
更加Python化的方法是引入一个琐碎的,一属性类。该函数接收该类的实例并操纵该属性。

There are no variables in Python

The key to understanding parameter passing is to stop thinking about “variables”. There are names and objects in Python and together they appear like variables, but it is useful to always distinguish the three.

  1. Python has names and objects.
  2. Assignment binds a name to an object.
  3. Passing an argument into a function also binds a name (the parameter name of the function) to an object.

That is all there is to it. Mutability is irrelevant to this question.

Example:

a = 1

This binds the name a to an object of type integer that holds the value 1.

b = x

This binds the name b to the same object that the name x is currently bound to. Afterward, the name b has nothing to do with the name x anymore.

See sections 3.1 and 4.2 in the Python 3 language reference.

How to read the example in the question

In the code shown in the question, the statement self.Change(self.variable) binds the name var (in the scope of function Change) to the object that holds the value 'Original' and the assignment var = 'Changed' (in the body of function Change) assigns that same name again: to some other object (that happens to hold a string as well but could have been something else entirely).

How to pass by reference

So if the thing you want to change is a mutable object, there is no problem, as everything is effectively passed by reference.

If it is an immutable object (e.g. a bool, number, string), the way to go is to wrap it in a mutable object.
The quick-and-dirty solution for this is a one-element list (instead of self.variable, pass [self.variable] and in the function modify var[0]).
The more pythonic approach would be to introduce a trivial, one-attribute class. The function receives an instance of the class and manipulates the attribute.


回答 8

我通常使用的一个简单技巧就是将其包装在列表中:

def Change(self, var):
    var[0] = 'Changed'

variable = ['Original']
self.Change(variable)      
print variable[0]

(是的,我知道这可能很不方便,但是有时候这样做很简单。)

A simple trick I normally use is to just wrap it in a list:

def Change(self, var):
    var[0] = 'Changed'

variable = ['Original']
self.Change(variable)      
print variable[0]

(Yeah I know this can be inconvenient, but sometimes it is simple enough to do this.)


回答 9

(编辑-布莱尔已更新了他的非常受欢迎的答案,因此现在是准确的)

我认为重要的是要注意,当前职位(布莱尔·康拉德(Blair Conrad))的票数最高,尽管其结果正确无误,但根据其定义,这是误导性的,也是不正确的。尽管有许多语言(例如C)允许用户通过引用传递或通过值传递,但Python并不是其中一种。

大卫·库纳波(David Cournapeau)的答案指出了真正的答案,并解释了为什么布莱尔·康拉德(Blair Conrad)的帖子中的行为似乎是正确的,而定义却不正确。

就Python按值传递的程度而言,所有语言都是按值传递的,因为必须发送一些数据(“值”或“引用”)。但是,这并不意味着Python就象C程序员会想到的那样按值传递。

如果您想要该行为,Blair Conrad的答案很好。但是,如果您想了解为什么Python既不按值传递也不按引用传递的细节,请阅读David Cournapeau的答案。

(edit – Blair has updated his enormously popular answer so that it is now accurate)

I think it is important to note that the current post with the most votes (by Blair Conrad), while being correct with respect to its result, is misleading and is borderline incorrect based on its definitions. While there are many languages (like C) that allow the user to either pass by reference or pass by value, Python is not one of them.

David Cournapeau’s answer points to the real answer and explains why the behavior in Blair Conrad’s post seems to be correct while the definitions are not.

To the extent that Python is pass by value, all languages are pass by value since some piece of data (be it a “value” or a “reference”) must be sent. However, that does not mean that Python is pass by value in the sense that a C programmer would think of it.

If you want the behavior, Blair Conrad’s answer is fine. But if you want to know the nuts and bolts of why Python is neither pass by value or pass by reference, read David Cournapeau’s answer.


回答 10

您在这里得到了一些非常好的答案。

x = [ 2, 4, 4, 5, 5 ]
print x  # 2, 4, 4, 5, 5

def go( li ) :
  li = [ 5, 6, 7, 8 ]  # re-assigning what li POINTS TO, does not
  # change the value of the ORIGINAL variable x

go( x ) 
print x  # 2, 4, 4, 5, 5  [ STILL! ]


raw_input( 'press any key to continue' )

You got some really good answers here.

x = [ 2, 4, 4, 5, 5 ]
print x  # 2, 4, 4, 5, 5

def go( li ) :
  li = [ 5, 6, 7, 8 ]  # re-assigning what li POINTS TO, does not
  # change the value of the ORIGINAL variable x

go( x ) 
print x  # 2, 4, 4, 5, 5  [ STILL! ]


raw_input( 'press any key to continue' )

回答 11

在这种情况下,var方法中标题为该变量Change的引用将分配给self.variable,然后您立即将一个字符串分配给var。它不再指向self.variable。以下代码段显示了如果修改由var和指向的数据结构self.variable(在本例中为列表)将会发生的情况:

>>> class PassByReference:
...     def __init__(self):
...         self.variable = ['Original']
...         self.change(self.variable)
...         print self.variable
...         
...     def change(self, var):
...         var.append('Changed')
... 
>>> q = PassByReference()
['Original', 'Changed']
>>> 

我相信其他人可以进一步澄清这一点。

In this case the variable titled var in the method Change is assigned a reference to self.variable, and you immediately assign a string to var. It’s no longer pointing to self.variable. The following code snippet shows what would happen if you modify the data structure pointed to by var and self.variable, in this case a list:

>>> class PassByReference:
...     def __init__(self):
...         self.variable = ['Original']
...         self.change(self.variable)
...         print self.variable
...         
...     def change(self, var):
...         var.append('Changed')
... 
>>> q = PassByReference()
['Original', 'Changed']
>>> 

I’m sure someone else could clarify this further.


回答 12

Python的传递分配方案与C ++的引用参数选项不太相同,但实际上与C语言(和其他语言)的参数传递模型非常相似:

  • 不变的参数有效地“ 按值 ” 传递。诸如整数和字符串之类的对象是通过对象引用传递的,而不是通过复制传递的,但是由于您无论如何都不能就地更改不可变对象,因此效果很像制作副本。
  • 可变参数有效地“ 通过指针 ” 传递。诸如列表和字典之类的对象也通过对象引用传递,这类似于C将数组作为指针传递的方式—可变对象可以在函数中就地更改,就像C数组一样。

Python’s pass-by-assignment scheme isn’t quite the same as C++’s reference parameters option, but it turns out to be very similar to the argument-passing model of the C language (and others) in practice:

  • Immutable arguments are effectively passed “by value.” Objects such as integers and strings are passed by object reference instead of by copying, but because you can’t change immutable objects in place anyhow, the effect is much like making a copy.
  • Mutable arguments are effectively passed “by pointer.” Objects such as lists and dictionaries are also passed by object reference, which is similar to the way C passes arrays as pointers—mutable objects can be changed in place in the function, much like C arrays.

回答 13

如您所言,您需要有一个可变对象,但让我建议您检查一下全局变量,因为它们可以帮助您甚至解决此类问题!

http://docs.python.org/3/faq/programming.html#what-are-the-rules-for-local-and-global-variables-in-python

例:

>>> def x(y):
...     global z
...     z = y
...

>>> x
<function x at 0x00000000020E1730>
>>> y
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'y' is not defined
>>> z
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'z' is not defined

>>> x(2)
>>> x
<function x at 0x00000000020E1730>
>>> y
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'y' is not defined
>>> z
2

As you can state you need to have a mutable object, but let me suggest you to check over the global variables as they can help you or even solve this kind of issue!

http://docs.python.org/3/faq/programming.html#what-are-the-rules-for-local-and-global-variables-in-python

example:

>>> def x(y):
...     global z
...     z = y
...

>>> x
<function x at 0x00000000020E1730>
>>> y
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'y' is not defined
>>> z
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'z' is not defined

>>> x(2)
>>> x
<function x at 0x00000000020E1730>
>>> y
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'y' is not defined
>>> z
2

回答 14

答案中有很多见解,但我认为此处没有明确提及其他要点。引用python文档https://docs.python.org/2/faq/programming.html#what-are-the-rules-for-local-and-global-variables-in-python

“在Python中,仅在函数内部引用的变量是隐式全局的。如果在函数体内的任何位置为变量分配了新值,则假定该变量是局部的。如果在函数内部分配了新值,该变量是隐式局部变量,您需要将其显式声明为“全局”变量,尽管起初有些令人惊讶,但片刻的考虑可以解释这一点:一方面,要求全局变量赋值可防止意外副作用。另一方面,如果所有全局引用都需要使用global,那么您将一直使用global,您必须将对内置函数或导入模块的组件的每个引用声明为全局。将破坏全球宣言对确定副作用的有用性。”

即使将可变对象传递给函数,这仍然适用。并且向我清楚地解释了在函数中分配给对象和对对象进行操作之间行为不同的原因。

def test(l):
    print "Received", l , id(l)
    l = [0, 0, 0]
    print "Changed to", l, id(l)  # New local object created, breaking link to global l

l= [1,2,3]
print "Original", l, id(l)
test(l)
print "After", l, id(l)

给出:

Original [1, 2, 3] 4454645632
Received [1, 2, 3] 4454645632
Changed to [0, 0, 0] 4474591928
After [1, 2, 3] 4454645632

因此,对未声明为全局变量的全局变量的分配将创建一个新的本地对象,并断开与原始对象的链接。

A lot of insights in answers here, but i think an additional point is not clearly mentioned here explicitly. Quoting from python documentation https://docs.python.org/2/faq/programming.html#what-are-the-rules-for-local-and-global-variables-in-python

“In Python, variables that are only referenced inside a function are implicitly global. If a variable is assigned a new value anywhere within the function’s body, it’s assumed to be a local. If a variable is ever assigned a new value inside the function, the variable is implicitly local, and you need to explicitly declare it as ‘global’. Though a bit surprising at first, a moment’s consideration explains this. On one hand, requiring global for assigned variables provides a bar against unintended side-effects. On the other hand, if global was required for all global references, you’d be using global all the time. You’d have to declare as global every reference to a built-in function or to a component of an imported module. This clutter would defeat the usefulness of the global declaration for identifying side-effects.”

Even when passing a mutable object to a function this still applies. And to me clearly explains the reason for the difference in behavior between assigning to the object and operating on the object in the function.

def test(l):
    print "Received", l , id(l)
    l = [0, 0, 0]
    print "Changed to", l, id(l)  # New local object created, breaking link to global l

l= [1,2,3]
print "Original", l, id(l)
test(l)
print "After", l, id(l)

gives:

Original [1, 2, 3] 4454645632
Received [1, 2, 3] 4454645632
Changed to [0, 0, 0] 4474591928
After [1, 2, 3] 4454645632

The assignment to an global variable that is not declared global therefore creates a new local object and breaks the link to the original object.


回答 15

这是pass by objectPython中使用的概念的简单解释(我希望如此)。
每当将对象传递给函数时,都会传递对象本身(Python中的对象实际上是您在其他编程语言中称为值的对象),而不是对该对象的引用。换句话说,当您调用时:

def change_me(list):
   list = [1, 2, 3]

my_list = [0, 1]
change_me(my_list)

正在传递实际对象[0,1](在其他编程语言中将其称为值)。因此,实际上该函数change_me将尝试执行以下操作:

[0, 1] = [1, 2, 3]

这显然不会改变传递给函数的对象。如果函数看起来像这样:

def change_me(list):
   list.append(2)

然后,该调用将导致:

[0, 1].append(2)

这显然会改变对象。这个答案很好地解释了。

Here is the simple (I hope) explanation of the concept pass by object used in Python.
Whenever you pass an object to the function, the object itself is passed (object in Python is actually what you’d call a value in other programming languages) not the reference to this object. In other words, when you call:

def change_me(list):
   list = [1, 2, 3]

my_list = [0, 1]
change_me(my_list)

The actual object – [0, 1] (which would be called a value in other programming languages) is being passed. So in fact the function change_me will try to do something like:

[0, 1] = [1, 2, 3]

which obviously will not change the object passed to the function. If the function looked like this:

def change_me(list):
   list.append(2)

Then the call would result in:

[0, 1].append(2)

which obviously will change the object. This answer explains it well.


回答 16

除了对这些东西如何在Python中工作的所有出色解释之外,我没有看到关于该问题的简单建议。正如您似乎确实要创建对象和实例一样,处理实例变量并更改它们的pythonic方法如下:

class PassByReference:
    def __init__(self):
        self.variable = 'Original'
        self.Change()
        print self.variable

    def Change(self):
        self.variable = 'Changed'

在实例方法中,通常引用self访问实例属性。通常__init__在实例方法中设置实例属性并对其进行读取或更改。这也是为什么您将selfals的第一个参数传递给def Change

另一个解决方案是创建一个像这样的静态方法:

class PassByReference:
    def __init__(self):
        self.variable = 'Original'
        self.variable = PassByReference.Change(self.variable)
        print self.variable

    @staticmethod
    def Change(var):
        var = 'Changed'
        return var

Aside from all the great explanations on how this stuff works in Python, I don’t see a simple suggestion for the problem. As you seem to do create objects and instances, the pythonic way of handling instance variables and changing them is the following:

class PassByReference:
    def __init__(self):
        self.variable = 'Original'
        self.Change()
        print self.variable

    def Change(self):
        self.variable = 'Changed'

In instance methods, you normally refer to self to access instance attributes. It is normal to set instance attributes in __init__ and read or change them in instance methods. That is also why you pass self als the first argument to def Change.

Another solution would be to create a static method like this:

class PassByReference:
    def __init__(self):
        self.variable = 'Original'
        self.variable = PassByReference.Change(self.variable)
        print self.variable

    @staticmethod
    def Change(var):
        var = 'Changed'
        return var

回答 17

即使该语言无法实现通过引用传递对象的小技巧,它也适用于Java,它是包含一项的列表。;-)

class PassByReference:
    def __init__(self, name):
        self.name = name

def changeRef(ref):
    ref[0] = PassByReference('Michael')

obj = PassByReference('Peter')
print obj.name

p = [obj] # A pointer to obj! ;-)
changeRef(p)

print p[0].name # p->name

这是一个丑陋的骇客,但确实有效。;-P

There is a little trick to pass an object by reference, even though the language doesn’t make it possible. It works in Java too, it’s the list with one item. ;-)

class PassByReference:
    def __init__(self, name):
        self.name = name

def changeRef(ref):
    ref[0] = PassByReference('Michael')

obj = PassByReference('Peter')
print obj.name

p = [obj] # A pointer to obj! ;-)
changeRef(p)

print p[0].name # p->name

It’s an ugly hack, but it works. ;-P


回答 18

我使用以下方法快速将几个Fortran代码转换为Python。是的,它并没有像最初的问题那样被引用,而是在某些情况下是一种简单的解决方法。

a=0
b=0
c=0
def myfunc(a,b,c):
    a=1
    b=2
    c=3
    return a,b,c

a,b,c = myfunc(a,b,c)
print a,b,c

I used the following method to quickly convert a couple of Fortran codes to Python. True, it’s not pass by reference as the original question was posed, but is a simple work around in some cases.

a=0
b=0
c=0
def myfunc(a,b,c):
    a=1
    b=2
    c=3
    return a,b,c

a,b,c = myfunc(a,b,c)
print a,b,c

回答 19

虽然按引用传递并不是最适合python的东西,并且不应该使用,但是有些变通办法实际上可以起作用,以获取当前分配给局部变量的对象,或者甚至从调用函数内部重新分配局部变量。

基本思想是拥有一个可以进行访问的函数,并且可以将其作为对象传递给其他函数或存储在类中。

一种方法是在包装函数中使用global(对于全局变量)或nonlocal(对于函数中的局部变量)。

def change(wrapper):
    wrapper(7)

x = 5
def setter(val):
    global x
    x = val
print(x)

相同的想法适用于读取和del设置变量。

对于正读,甚至有一种更短的使用方法,lambda: x该方法返回可调用对象,而被调用时将返回x的当前值。这有点像遥远的过去在语言中使用的“按名称呼叫”。

传递3个包装器来访问变量有点麻烦,因此可以将它们包装到具有proxy属性的类中:

class ByRef:
    def __init__(self, r, w, d):
        self._read = r
        self._write = w
        self._delete = d
    def set(self, val):
        self._write(val)
    def get(self):
        return self._read()
    def remove(self):
        self._delete()
    wrapped = property(get, set, remove)

# left as an exercise for the reader: define set, get, remove as local functions using global / nonlocal
r = ByRef(get, set, remove)
r.wrapped = 15

借助Python的“反射”支持,可以获得一个对象,该对象能够在给定范围内重新分配名称/变量,而无需在该范围内显式定义函数:

class ByRef:
    def __init__(self, locs, name):
        self._locs = locs
        self._name = name
    def set(self, val):
        self._locs[self._name] = val
    def get(self):
        return self._locs[self._name]
    def remove(self):
        del self._locs[self._name]
    wrapped = property(get, set, remove)

def change(x):
    x.wrapped = 7

def test_me():
    x = 6
    print(x)
    change(ByRef(locals(), "x"))
    print(x)

在这里,ByRef该类包装了字典访问权限。因此,对属性的访问将wrapped转换为所传递字典中的项目访问。通过传递内建的结果locals和局部变量的名称,最终访问局部变量。从3.5版开始的python文档建议更改字典可能不起作用,但似乎对我有用。

While pass by reference is nothing that fits well into python and should be rarely used there are some workarounds that actually can work to get the object currently assigned to a local variable or even reassign a local variable from inside of a called function.

The basic idea is to have a function that can do that access and can be passed as object into other functions or stored in a class.

One way is to use global (for global variables) or nonlocal (for local variables in a function) in a wrapper function.

def change(wrapper):
    wrapper(7)

x = 5
def setter(val):
    global x
    x = val
print(x)

The same idea works for reading and deleting a variable.

For just reading there is even a shorter way of just using lambda: x which returns a callable that when called returns the current value of x. This is somewhat like “call by name” used in languages in the distant past.

Passing 3 wrappers to access a variable is a bit unwieldy so those can be wrapped into a class that has a proxy attribute:

class ByRef:
    def __init__(self, r, w, d):
        self._read = r
        self._write = w
        self._delete = d
    def set(self, val):
        self._write(val)
    def get(self):
        return self._read()
    def remove(self):
        self._delete()
    wrapped = property(get, set, remove)

# left as an exercise for the reader: define set, get, remove as local functions using global / nonlocal
r = ByRef(get, set, remove)
r.wrapped = 15

Pythons “reflection” support makes it possible to get a object that is capable of reassigning a name/variable in a given scope without defining functions explicitly in that scope:

class ByRef:
    def __init__(self, locs, name):
        self._locs = locs
        self._name = name
    def set(self, val):
        self._locs[self._name] = val
    def get(self):
        return self._locs[self._name]
    def remove(self):
        del self._locs[self._name]
    wrapped = property(get, set, remove)

def change(x):
    x.wrapped = 7

def test_me():
    x = 6
    print(x)
    change(ByRef(locals(), "x"))
    print(x)

Here the ByRef class wraps a dictionary access. So attribute access to wrapped is translated to a item access in the passed dictionary. By passing the result of the builtin locals and the name of a local variable this ends up accessing a local variable. The python documentation as of 3.5 advises that changing the dictionary might not work but it seems to work for me.


回答 20

给定python处理值和对其的引用的方式,您可以引用任意实例属性的唯一方法是按名称进行:

class PassByReferenceIsh:
    def __init__(self):
        self.variable = 'Original'
        self.change('variable')
        print self.variable

    def change(self, var):
        self.__dict__[var] = 'Changed'

当然,在实际代码中,您将在dict查找上添加错误检查。

given the way python handles values and references to them, the only way you can reference an arbitrary instance attribute is by name:

class PassByReferenceIsh:
    def __init__(self):
        self.variable = 'Original'
        self.change('variable')
        print self.variable

    def change(self, var):
        self.__dict__[var] = 'Changed'

in real code you would, of course, add error checking on the dict lookup.


回答 21

由于字典是通过引用传递的,因此可以使用dict变量在其中存储任何引用的值。

# returns the result of adding numbers `a` and `b`
def AddNumbers(a, b, ref): # using a dict for reference
    result = a + b
    ref['multi'] = a * b # reference the multi. ref['multi'] is number
    ref['msg'] = "The result: " + str(result) + " was nice!" # reference any string (errors, e.t.c). ref['msg'] is string
    return result # return the sum

number1 = 5
number2 = 10
ref = {} # init a dict like that so it can save all the referenced values. this is because all dictionaries are passed by reference, while strings and numbers do not.

sum = AddNumbers(number1, number2, ref)
print("sum: ", sum)             # the return value
print("multi: ", ref['multi'])  # a referenced value
print("msg: ", ref['msg'])      # a referenced value

Since dictionaries are passed by reference, you can use a dict variable to store any referenced values inside it.

# returns the result of adding numbers `a` and `b`
def AddNumbers(a, b, ref): # using a dict for reference
    result = a + b
    ref['multi'] = a * b # reference the multi. ref['multi'] is number
    ref['msg'] = "The result: " + str(result) + " was nice!" # reference any string (errors, e.t.c). ref['msg'] is string
    return result # return the sum

number1 = 5
number2 = 10
ref = {} # init a dict like that so it can save all the referenced values. this is because all dictionaries are passed by reference, while strings and numbers do not.

sum = AddNumbers(number1, number2, ref)
print("sum: ", sum)             # the return value
print("multi: ", ref['multi'])  # a referenced value
print("msg: ", ref['msg'])      # a referenced value

回答 22

Python中的按引用传递与C ++ / Java中的按引用传递概念完全不同。

  • Java&C#:原始类型(包括字符串)通过值(副本)传递,引用类型通过引用(地址副本)传递,因此对调用者可见的所有被调用函数中的参数更改。
  • C ++:允许按引用传递或按值传递。如果参数是通过引用传递的,则可以根据是否以const形式传递参数来对其进行修改。但是,无论是否为const,该参数都将保留对对象的引用,并且无法将引用分配为指向所调用函数内的另一个对象。
  • Python: Python是“按对象传递引用”,通常这样说:“对象引用按值传递。” [阅读此处] 1。调用者和函数都引用相同的对象,但是函数中的参数是一个新变量,它仅在调用者中保存对象的副本。像C ++一样,可以在函数中修改或不修改参数-这取决于传递的对象的类型。例如; 不变的对象类型不能在调用的函数中修改,而可变的对象可以更新或重新初始化。更新或重新分配/重新初始化可变变量之间的关键区别在于,更新后的值会反映在被调用函数中,而重新初始化后的值则不会。将新对象分配给可变变量的范围是python中函数的局部范围。@ blair-conrad提供的示例很好地理解了这一点。

Pass-By-Reference in Python is quite different from the concept of pass by reference in C++/Java.

  • Java&C#: primitive types(include string)pass by value(copy), Reference type is passed by reference(address copy) so all changes made in the parameter in the called function are visible to the caller.
  • C++: Both pass-by-reference or pass-by-value are allowed. If a parameter is passed by reference, you can either modify it or not depending upon whether the parameter was passed as const or not. However, const or not, the parameter maintains the reference to the object and reference cannot be assigned to point to a different object within the called function.
  • Python: Python is “pass-by-object-reference”, of which it is often said: “Object references are passed by value.”[Read here]1. Both the caller and the function refer to the same object but the parameter in the function is a new variable which is just holding a copy of the object in the caller. Like C++, a parameter can be either modified or not in function – This depends upon the type of object passed. eg; An immutable object type cannot be modified in the called function whereas a mutable object can be either updated or re-initialized. A crucial difference between updating or re-assigning/re-initializing the mutable variable is that updated value gets reflected back in the called function whereas the reinitialized value does not. Scope of any assignment of new object to a mutable variable is local to the function in the python. Examples provided by @blair-conrad are great to understand this.

回答 23

由于您的示例碰巧是面向对象的,因此可以进行以下更改以获得相似的结果:

class PassByReference:
    def __init__(self):
        self.variable = 'Original'
        self.change('variable')
        print(self.variable)

    def change(self, var):
        setattr(self, var, 'Changed')

# o.variable will equal 'Changed'
o = PassByReference()
assert o.variable == 'Changed'

Since your example happens to be object-oriented, you could make the following change to achieve a similar result:

class PassByReference:
    def __init__(self):
        self.variable = 'Original'
        self.change('variable')
        print(self.variable)

    def change(self, var):
        setattr(self, var, 'Changed')

# o.variable will equal 'Changed'
o = PassByReference()
assert o.variable == 'Changed'

回答 24

您只能将一个空类用作存储参考对象的实例,因为内部对象属性存储在实例字典中。参见示例。

class RefsObj(object):
    "A class which helps to create references to variables."
    pass

...

# an example of usage
def change_ref_var(ref_obj):
    ref_obj.val = 24

ref_obj = RefsObj()
ref_obj.val = 1
print(ref_obj.val) # or print ref_obj.val for python2
change_ref_var(ref_obj)
print(ref_obj.val)

You can merely use an empty class as an instance to store reference objects because internally object attributes are stored in an instance dictionary. See the example.

class RefsObj(object):
    "A class which helps to create references to variables."
    pass

...

# an example of usage
def change_ref_var(ref_obj):
    ref_obj.val = 24

ref_obj = RefsObj()
ref_obj.val = 1
print(ref_obj.val) # or print ref_obj.val for python2
change_ref_var(ref_obj)
print(ref_obj.val)

回答 25

由于似乎没有地方提到,例如C ++等已知的模拟引用的方法是使用“更新”函数并传递该函数而不是实际变量(或更确切地说,是“名称”):

def need_to_modify(update):
    update(42) # set new value 42
    # other code

def call_it():
    value = 21
    def update_value(new_value):
        nonlocal value
        value = new_value
    need_to_modify(update_value)
    print(value) # prints 42

这对于“仅引用”或在具有多个线程/进程的情况下(通过使更新功能线程/多重处理安全)最有用。

显然,以上内容不允许读取值,而只能对其进行更新。

Since it seems to be nowhere mentioned an approach to simulate references as known from e.g. C++ is to use an “update” function and pass that instead of the actual variable (or rather, “name”):

def need_to_modify(update):
    update(42) # set new value 42
    # other code

def call_it():
    value = 21
    def update_value(new_value):
        nonlocal value
        value = new_value
    need_to_modify(update_value)
    print(value) # prints 42

This is mostly useful for “out-only references” or in a situation with multiple threads / processes (by making the update function thread / multiprocessing safe).

Obviously the above does not allow reading the value, only updating it.