了解Python中的位置参数

时间:2019-02-15 12:07:16

标签: python python-3.x

对于以下Python脚本:

from sys import argv

script, input_encoding, error = argv

def main(language_file, encoding, errors):

    line = language_file.readline()

    if line:

        print_line(line, encoding, errors)
        return main(language_file, encoding, errors)

def print_line(line, encoding, errors):
    next_lang = line.strip()
    raw_bytes = next_lang.encode(encoding, errors=errors)
    cooked_string = raw_bytes.decode(encoding, errors=errors)

    print(raw_bytes, "<===>", cooked_string)

languages = open("languages.txt", encoding="utf-8")

main(languages, input_encoding, error)

看着main函数,我不明白下面这一行:

print_line(line, encoding, errors)

为什么我们要调用print_line函数并将其名称完全相同的参数传递给它?

print_line()

当我尝试调用print_line()参数而不传递参数时,Python输出:

  

print_line()缺少3个必需的位置参数:“ line”,   “编码”和“错误”

4 个答案:

答案 0 :(得分:0)

OP: print_line()缺少3个必需的位置参数:“行”,“编码”和“错误”

该错误很明显,因为它是函数print_line()的定义方式。

此外:

def print_line(line, encoding, errors):
    print(line, encoding, errors)


line = 1
encoding = 2
errors = 3

print_line(errors, encoding, line)

输出:

3 2 1
  

注意:它是位置信息,而不是命名参数

编辑:1

def abc(a,b,c=2):

return a+b+c

abc(1,2) #both positional argument and c is default

5

abc(2, b=3) # positional, named and again c is default

7

abc(a=2,b=4) # both named argument and c is default

8

编辑2:

OP:请问位置论证的目的是什么?

好吧..

简短的回答:位置参数是任何未作为key=value对提供的参数。

不幸的是,要理解这意味着什么。

在整个编程社区中,尤其是在Python文档中,“自变量”一词的使用有些不精确。

从技术上讲,参数是传递给函数的参数,参数是定义为这些参数的名称/占位符的参数。

所以,当我这样定义一个函数时:

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

...并这样称呼:

foo(1,3)

...然后a和b是我的参数,而1和3是给定函数调用的参数。

现在这是一个小问题。人们通常将a和b称为其函数的“参数”,因为它们实际上是在函数执行时会包含参数的名称(参数)。

现在,到此为止,您已经了解Python支持四类参数:“ required positional”(您所见过的几乎任何其他编程语言都可以使用),“ optional” ...或“ defaulted”。 ..带有指定默认值的位置参数,“ star args”(类似于某些其他语言(如C / C ++和Java)对VARARGS的支持)和“ kwargs”。

后者几乎是Python所独有的(尽管Ruby具有非常相似的支持)。

因此,您可以使用以下参数列表来定义函数:

def bar(a, b, c=None, d=[], *e,  **opts):
    '''bar takes all sorts of arguments
    '''
    results = dict()
    results['positional 1'] = a
    results['positional 2'] = b
    results['sum']=a+b
    results['optional'] = list()
    for each in e:
        results['optional'].append(each)
    if c is not None:
        results['default over-ridden']=c
    else:
        results['default']='no arg supplied for parameter c'
    d.append(results)
    if 'verbose' in opts and opts['verbose']:
        for k,v in results:
            print '%s:%s' % (k, v)
    return (results, d)

...这个颇为人为的示例包含几个常规的传统位置参数(a和b)以及可选的第三和第四个参数(其中一个默认为特殊的Python单例值None)如果只用两个参数调用bar(),而另一个参数将默认为列表),以及可选的可变数量的其他参数(通过名为“ e”的参数传递到我们的函数中),最后是bar ()可以接受任意数量的“关键字”参数(作为键值对传递给它,并通过参数“ opts”进行引用(在我的示例中)。

那里有很多东西可以消化。首先是a和b。这些就像您在大多数编程语言中看到的一样。如果仅使用一个参数调用此函数,则会出错,因为该函数需要两个参数。然后有c和d ...这些参数可以提供参数...但如果仅用两个必需的参数调用该函数,则参数c将引用“ None”,而d将引用列表。是在定义函数时实例化的!

哇!重读最后一点,因为对于那些错误地使用默认为可变类型(列表,字典,集合或您自定义定义的类的大多数实例)的参数来定义函数的人来说,这是一个常见的困惑源。

在Python中定义函数时,解释器正在执行代码。它正在执行def语句并评估参数(它们成为函数的参数)。因此,Python虚拟机在定义函数时实例化一个列表([] ---空列表文字)。现在,参数(在我的示例中为e)已绑定到该列表,就像任何Python“变量”(名称)都已绑定到任何其他对象一样。只要使用三个或更少的参数调用bar(),就可以访问它所引用的对象。

这是一个棘手的问题:每当您调用带有三个以上参数的bar时,参数e(在该特定调用的持续时间内)将被绑定到第四个参数。在该通话期间,基础列表将被隐藏。这样的默认列表对象包含在一个闭包中,通常在该函数外部是完全不可访问的。 (在此示例中,我在return语句中包括了对它的引用,显示了如果选择的话我们如何导出对该封闭对象的引用)。

因此,如果我向bar()传递了四个参数,则它将尝试使用该对象的“ append”方法修改通过其“ e”参数引用的对象。 (显然,这将失败并为任何不支持append方法的对象引发异常)。每次我只用三个参数调用bar()时,我都会在其闭包内修改该封闭的列表对象。

这可能很有用,但很少是新程序员在学习Python时所期望的。因此,通常,请勿将可变对象定义为默认参数。当您充分理解语义后,便知道何时打破该规则。

出于某种原因,我将“ None”用作其他参数的默认值。对于要为其具有默认值的任何参数,将“ None”用作默认值,然后显式测试“ is None”并在函数体内提供自己的默认值通常很有用。这样,您可以区分默认参数值和显式传递给您的任何参数(碰巧与您的默认值匹配)。 (这还将防止您可能如前所述无意中创建可变闭包。函数主体中发生的赋值/绑定将为达到该条件的每个调用导致新的实例化。)

因此,我们介绍了必需的位置参数,以及提供了默认值的位置参数(因此是可选的位置参数)。

使用参数'e',我们看到Python支持可变数量的参数。第四位之后指定的所有参数都将收集到一个元组中,并通过我们的参数'e'传递给我们...除了以下形式的任何参数:this = that。

最后带来了我们。以“选择”。按照惯例,Python程序员会将这个参数称为“ kwargs”(关键字/单词参数)。我将其命名为“ opts”以强调这一点,即“ kwargs”仅是常规术语,并且从技术上来说有点令人困惑,因为正如我在本文中一直在努力的那样,它是指任何可能通过某些函数调用作为参数传递的关键字参数。

可以编写所有函数,使它们不使用任何位置参数,并且仅使用“键/字参数”参数进行定义。然后,您可以确保函数的调用者每次调用您时,必须始终阐明将哪个参数绑定到哪个名称。如果您的函数在使用错误顺序的参数调用的任何情况下都可能造成灾难性的后果,那么这会很方便。

我意识到这令人困惑……我绝对建议您使用不同的函数定义和各种各样的调用,以了解它们如何相互作用。

我还要注意另外一个可以咬你的“ gotchya”。您的呼叫者无法通过名称与参数名称冲突的键传递kwargs(优化)值。尝试使用诸如bar(1,2,3,4,a=99)之类的参数列表调用bar(),您将得到如下异常:"TypeError: bar() got multiple values for keyword argument 'a'"

Python通过管理名称空间(如字典)将参数解析为参数。试图提供一个关键字/单词参数,任何与您的参数名称匹配的关键字都会产生歧义...从而引发异常。

我还将在这个已经很麻烦的答案中添加两个注释。

在Python中调用函数时,您可以像这样传递参数:

myargs=(1,2,3)
bar(*myargs)

...,这将被视为您用bar(1,2,3)进行了调用---或好像您进行了以下函数调用:apply(bar, myargs)

实际上,过去曾经必须使用apply()函数才能完成这一间接层(可以使用*foo参数定义函数但不使用{{ 1}}个参数)。

(在函数 calls 上添加了* args语法,大大替代了Python的apply()内置函数的使用。)

...,最后可以使用以下语法传递*foo的字典:

kwargs

...或与我之前的示例结合在一起

mykwargs={'z':99, 'whatever':'yikes'}
bar(1,2,3, **mykwargs)

因此,至关重要的是要理解在定义函数时*和**以及调用它们时的含义之间的区别。如果您理解 arguments parameters 之间的区别(尽管更常使用“ arguments”一词),则这些含义是相互补充和直观的。

答案 1 :(得分:0)

  

我们为什么要调用print_line函数并将其名称完全相同的参数传递给它?

那真的只是一个巧合。以下与您的示例完全相同:

)

一切都归结为“范围”。 print_line的函数定义声明它具有三个(位置)参数,可以在函数内部将其称为“ line”,“ encoding”和“ errors”。

从main对print_line的函数调用向调用添加了三个参数,这些参数引用了代码中该点上的现有变量或参数:line引用在那里创建的变量;编码和编码是指主函数本身的参数。

答案 2 :(得分:0)

该函数需要三个参数,通常,您可以按指定的顺序提供它们。

Python允许您按照自己喜欢的顺序传递它们,

print_line(encoding='ascii', line='hello', errors=None)

甚至

my_dict = {
    'line': 'privet, mir!',
    'errors': None,
    'encoding': 'utf-8'
}
print_line(**my_dict)

但是,该函数需要所有这些参数。您必须以某种方式传递它们,否则会收到一条错误消息,就像您注意到的那样。

变量的作用域是局部的:def中的任何内容对于其定义的函数而言都是局部的,并且与该块外部具有相同名称的其他变量分开。

可以通过定义默认值使参数成为可选参数。可选参数必须始终位于Python中的强制参数之后。

def another_fun(confetti, fireworks, bassoons=None, candy='sugar fluff')

如果仅使用两个参数调用another_fun,则bassoons将默认为None,而candy将被设置为字符串'sugar fluff'

答案 3 :(得分:-1)

  

为什么要调用print_line函数

好吧,显然要执行它;-)

  

并向其传递参数

因为该函数已定义为接受参数-如果您不提供需要处理的内容,它将无法完成工作。

  

其名称与参数完全相同?

这实际上是完全不相关的-恰好发生main()中的变量被命名为与函数的参数相同(这很有意义,因为我们讨论的是相同的事物-“一行文本”,一个编码名称和一个描述如何处理编码错误的值),但是您可以随便使用任意名称来传递乱码(未命名)值或变量,它的作用相同。

功能是(主要)独立的工作单元。它通常使用一些内部(“局部”)变量,只有该变量可以看到并且仅在函数执行期间存在。它还通常使用参数:必须由调用者传递的值,并绑定到匹配的参数名称(在我们的示例中,第一个值将绑定到line名称,第二个值绑定到{{1 }}名称等)。本地名称(本地变量和参数)与在调用方范围内知道这些值的名称完全无关(即使它们绑定到名称,正如我所说的,您也可以传递文字值或其他匿名对象)< / p>

  

当我尝试在不传递参数的情况下调用print_line()参数时,Python输出的是“ print_line()缺少3个必需的位置参数:'line','encoding'和'errors'”

是的,当然。该函数需要三个自变量,因此您必须传递三个自变量和简单自变量。在调用者作用域中,您有三个具有相同名称的局部变量(encoding函数)将不会自动为您自动填充这些参数,而main函数对此一无所知反正呼叫者的范围。

请注意,术语“位置”和“命名”参数主要是指您如何传递本身(通过位置(默认值),我已经在上文中进行了说明)或通过名称(即print_line,它允许您以不同的顺序传递参数,或通过动态构建的字典等传递参数,但是在进一步操作之前,您首先需要了解函数,参数和范围的概念。

我强烈建议您去做一个有a chapter on functions的官方教程-它主要是针对已经有一定编程经验的人(不是CS101课程),但是仍然有很好的解释。