为了账号安全,请及时绑定邮箱和手机立即绑定

理解 Python 中的位置参数

理解 Python 中的位置参数

ABOUTYOU 2021-11-09 19:54:06
对于以下 Python 脚本:from sys import argvscript, input_encoding, error = argvdef 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”、“encoding”和“errors”
查看完整描述

3 回答

?
慕斯709654

TA贡献1840条经验 获得超5个赞

OP:print_line() 缺少 3 个必需的位置参数:“line”、“encoding”和“errors”


错误很明显,因为它是函数 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 支持四类参数:“必需位置”(您在几乎任何其他编程语言中看到的那种)、“可选”……或“默认”……位置指定了一些默认值的参数、“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),以及可选的第三和第四个参数(None如果 bar() 被调用,其中一个将默认为特殊的 Python 单例值两个参数,另一个默认为列表)以及可选的可变数量的附加参数(通过名为“e”的参数传递到我们的函数中),最后,bar() 可以接受任意数量的“关键字”参数(作为键值对传递给它并通过参数“opts”引用(在我的示例中)。


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


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


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


这是棘手的一点:任何时候你用三个以上的参数调用 bar 时,参数 e 将被绑定(在那个特定调用的持续时间内)到第四个参数。在该调用期间,底层列表将被隐藏。这样的默认列表对象包含在一个闭包中,通常在函数之外是完全不可访问的。(在这个例子中,我在我的 return 语句中包含了一个对它的引用,展示了如果我们选择,我们可以如何导出对那个封闭对象的引用)。


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


这可能很有用,但很少是新程序员在学习 Python 时所期望的。因此,作为一项规则,不要使用可变对象作为参数的默认值来定义函数。当您充分理解语义时,您就知道何时打破该规则。


出于某种原因,我使用“无”作为其他参数的默认值。使用“None”作为您想要为其设置默认值的任何参数的默认值通常很有用,然后显式测试“is None”并从您的函数体内提供您自己的默认值。通过这种方式,您可以区分默认参数值和明确传递给您的任何参数(并且碰巧与您的默认值匹配)。(这也将防止您可能无意中创建一个可变闭包的情况,如前所述。在您的函数体中发生的分配/绑定将导致每次调用达到该条件时都进行新的实例化)。


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


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


这给我们带来了,最后。“选择”。通常 Python 程序员会将此参数称为“ kwargs”(关键字/字参数)。我将它命名为“opts”是为了强调这一点,即“ kwargs”只是一个传统的术语,从技术上讲,有点令人困惑,因为正如我在整篇文章中所阐述的,它是一个参数,它指的是任何可能的关键字参数已通过某些函数调用作为参数传递。


可以编写所有函数,使其不接受位置参数,并且仅使用“关键字/单词参数”参数进行定义。然后,您可以确保每次调用您的函数时,您的函数的调用者必须始终拼出哪个参数绑定到哪个名称。如果您的函数在以错误顺序调用参数的任何情况下可能会产生灾难性后果,这可能会很方便。


我意识到这令人困惑……我绝对建议您尝试使用不同的函数定义和各种调用来查看它们如何相互作用。


我还要注意一个额外的“gotchya”,它可以咬你。您的调用者不能使用名称与您的参数名称冲突的键传递 kwargs (opts) 值。尝试使用如下参数列表调用 bar() ,bar(1,2,3,4,a=99)您将得到如下异常: "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参数定义函数但不能用参数调用它们的时候*foo)。


(在函数调用上添加 *args 语法在很大程度上取代了 Python 的 apply() 内置函数的使用)。


...最后,可以传递kwargs使用此语法的字典:


mykwargs={'z':99, 'whatever':'yikes'}

bar(1,2,3, **mykwargs)

...或者结合我之前的例子:


bar(*myargs, **mykwargs)

因此,了解定义函数时 * 和 ** 的含义与调用函数时的含义之间的区别至关重要。如果您了解参数和参数之间的区别(尽管术语“参数”更常用),则这些含义是相互补充且直观的。


查看完整回答
反对 回复 2021-11-09
?
蓝山帝景

TA贡献1843条经验 获得超7个赞

为什么我们调用 print_line 函数并向它传递与参数名称完全相同的参数?


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


from sys import argv


script, input_encoding, error = argv


def main(language_file, what_encoding_do_you_want, list_of_errors):


    next_line = language_file.readline()


    if next_line:


        print_line(next_line, what_encoding_do_you_want, list_of_errors)

        return main(language_file, what_encoding_do_you_want, list_of_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)

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


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


查看完整回答
反对 回复 2021-11-09
?
守着一只汪

TA贡献1872条经验 获得超3个赞

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


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设置为 string 'sugar fluff'。


查看完整回答
反对 回复 2021-11-09
  • 3 回答
  • 0 关注
  • 169 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信