ch18参数(python)

#传递参数
参数的传递是通过自动将对象赋值给本地变量名来实现的,引用是以指针形式实现的
函数内部的参数名的赋值不会影响调用者
改变函数的可变对象参数也许会对调用者有影响

#python的通过赋值传递机制与C语言的参数传递模型相当类似
不可变参数"通过值"进行传递。像整数和字符串这样的对象是通过对象引用而不是拷贝进行传递的,但是因为无论怎样都不能在原处改变不可变对象,实际的效果就像创建了一份拷贝
可变参数是通过"指针"进行传递的。例如列表和字典对象也是通过对象引用进行传递的。

#参数和共享引用
>>> def f(a):                 # a is assigned to (references) passed object
...     a = 99                # Changes local variable a only  a是一个本地变量,a重新赋值为一个不同的变量
...
>>> b = 88
>>> f(b)                      # a and b both reference same 88 initially
>>> print(b)                  # b is not changed
88


#传递可变参数
>>> def changer(a, b):        # Arguments assigned references to objects
...     a = 2                 # Changes local name's value only
...     b[0] = 'spam'         # Changes shared object in-place
...
>>> X = 1
>>> L = [1, 2]                # Caller
>>> changer(X, L)             # Pass immutable and mutable objects
>>> X, L                      # X is unchanged, L is different!
(1, ['spam', 2])
#自动对传入的参数进行赋值的效果与运行一系列简单的赋值语句是相同的。

>>> X = 1
>>> a = X               # They share the same object
>>> a = 2               # Resets 'a' only, 'X' is still 1
>>> print(X)
1



>>> L = [1, 2]
>>> b = L               # They share the same object
>>> b[0] = 'spam'       # In-place change: 'L' sees the change too
>>> print(L)
['spam', 2]


#避免对可变参数的修改

#传入一份拷贝
L = [1, 2]
changer(X, L[:])        # Pass a copy, so our 'L' does not change


#在函数内部拷贝
def changer(a, b):
   b = b[:]             # Copy input list so we don't impact caller
   a = 2
   b[0] = 'spam'        # Changes our list copy only

#将可变对象转换为不可变对象
L = [1, 2]
changer(X, tuple(L))    # Pass a tuple, so changes are errors
#这种解决方案的局限性大

#对参数输出进行模拟
函数返回多个值,通过元组或其他集合类型
>>> def multiple(x, y):
...     x = 2               # Changes local names only
...     y = [3, 4]
...     return x, y         # Return new values in a tuple  返回一个元组
...
>>> X = 1
>>> L = [1, 2]
>>> X, L = multiple(X, L)   # Assign results to caller's names
>>> X, L
(2, [3, 4])
#这段代码的实际效果就是通过明确的赋值模拟了其他语言的输出参数,X和L在调用后发生了改变,但是这仅仅是因为代码编写而已。


#python2.6中,python3中不支持
def f((a, (b, c))):

#在python3中,可以这样写代码
def f(T): (a, (b, c)) = T


#特定的参数匹配模型
#位置:从左到右进行匹配
#关键字参数:通过参数名进行匹配
#默认参数:为没有传入值的参数定义参数值
#可变参数:收集任意多基于位置或关键字的参数,函数能够使用特定的参数,它们以字符*开头,收集任意多的额外参数(这个特性常常叫做可变参数,类似C语言中的可变参数特性,也能够支持可变长度参数列表)
#可变参数解包:传递任意多的基于位置或关键字的参数。
调用者能够再使用*语法去将参数集合打散,分成参数。这个*与在函数头部的*恰恰相反。
#Keyword-only参数:参数必须按照名称传递

#细节
#在函数调用中,参数必须以此顺序出现:任何位置参数(value),后面跟着任何关键词参数(name=value)和*sequence形式的组合,后面跟着**dict的形式
#在函数头部中,参数必须以此顺序出现,任意一般参数(value),跟着任何默认参数(name=value),如果有的话,后面是*name(或者在python3中是*)的形式,后面跟着任何name或name=value keyword-only参数(在python3中),后面跟着**name形式

#python内部使用以下的步骤来在赋值前进行参数匹配的:匹配规则
1:通过位置分配非关键字参数
2:通过匹配变量名分配关键字参数
3:其他额外的非关键字参数分配到*name元组中
4:其他额外的关键字参数分配到**name字典中
5:使用默认值分配在头部未得到分配的参数

#在python3中,函数头部中的参数名称也可以有一个注解值:特定形式如name:value(或者给出默认值的话是name:value=default形式)
函数自身也可以有一个注解值,以def f()->value的形式给出。

#关键字参数和默认参数的实例
#位置参数
>>> def f(a, b, c): print(a, b, c)
...

#位置参数
>>> f(1, 2, 3)
1 2 3

#关键字参数
#在函数调用中,使用关键字形式调用
>>> f(c=3, b=2, a=1)
1 2 3

#在函数调用中,混合位置参数和关键字参数
>>> f(1, c=3, b=2)
1 2 3

#关键字的调用形式更文档化,更明了
func(name='Bob', age=40, job='dev')


#函数定义时的默认参数
>>> def f(a, b=2, c=3): print(a, b, c)
...


>>> f(1)   #a赋值为1,b,c使用默认值
1 2 3
>>> f(a=1) #a赋值为1,b,c使用默认值
1 2 3


>>> f(1, 4)    #4会覆盖b的默认值2
1 4 3
>>> f(1, 4, 5) #4和5会覆盖默认值
1 4 5


>>> f(1, c=6)  #a通过位置得到1,c通过关键字得到6
1 2 6

#不要被在一个函数头部和一个函数调用中的特定的name=value的语法搞混,
在调用中,这意味着通过变量名进行匹配的关键字,而在函数头部中,它为一个可选的参数定义了默认值,无论哪种情况,这都不是一个赋值语句。

#关键词参数和默认参数的混合
def func(spam, eggs, toast=0, ham=0):   # First 2 required
    print((spam, eggs, toast, ham))

func(1, 2)                              # Output: (1, 2, 0, 0)
func(1, ham=1, eggs=0)                  # Output: (1, 0, 0, 1)
func(spam=1, eggs=0)                    # Output: (1, 0, 0, 0)
func(toast=1, eggs=2, spam=3)           # Output: (3, 2, 1, 0)
func(1, 2, 3, 4)                        # Output: (1, 2, 3, 4)


#任意参数的实例
#最后两种匹配扩展,*和**

#收集参数:在函数定义中,收集不匹配的位置参数
>>> def f(*args): print(args)
...


>>> f()
()
>>> f(1)
(1,)
>>> f(1, 2, 3, 4)
(1, 2, 3, 4)

#收集关键字参数,转换为字典,这个字典之后能够通过一般的字典工具进行处理。
>>> def f(**args): print(args)
...
>>> f()
{}
>>> f(a=1, b=2)
{'a': 1, 'b': 2}

#函数定义中,结合*和**
>>> def f(a, *pargs, **kargs): print(a, pargs, kargs)
...
>>> f(1, 2, 3, x=1, y=2)
1 (2, 3) {'y': 2, 'x': 1}


#函数调用的时候,使用*和**,表示解包参数

##函数调用时,使用*,解包
>>> def func(a, b, c, d): print(a, b, c, d)
...
>>> args = (1, 2)
>>> args += (3, 4)
>>> func(*args)   #函数调用时,使用*
1 2 3 4

#函数调用时,使用**,解包一个字典
>>> args = {'a': 1, 'b': 2, 'c': 3}
>>> args['d'] = 4
>>> func(**args)
1 2 3 4


#函数调用的时候灵活混合普通参数,位置参数以及关键字参数
>>> func(*(1, 2), **{'d': 4, 'c': 4})
1 2 4 4

>>> func(1, *(2, 3), **{'d': 4})
1 2 3 4

>>> func(1, c=3, *(2,), **{'d': 4})
1 2 3 4

>>> func(1, *(2, 3), d=4)
1 2 3 4

>>> f(1, *(2,), c=3, **{'d':4})
1 2 3 4
#不要混肴函数定义和函数调用时*/**的语法,在函数定义时,意味着收集任意数量的参数,在函数调用时,它解包任意数量的参数

func(*open('aa.txt'))的形式也是可以的,因为调用中的*pargs形式是一个迭代环境,因此技术上它可以接受任何可迭代的对象


#应用函数通用性,当你无法预计参数列表的时候,这种varargs调用语法很有用。
if <test>:
    action, args = func1, (1,)             # Call func1 with 1 arg in this case
else:
    action, args = func2, (1, 2, 3)        # Call func2 with 3 args here
...
action(*args)                              # Dispatch generically



>>> args = (2,3)
>>> args += (4,)
>>> args
(2, 3, 4)
>>> func(*args)


#应用函数通用性的例子:
def tracer(func, *pargs, **kargs):         # Accept arbitrary arguments
    print('calling:', func.__name__)
    return func(*pargs, **kargs)           # Pass along arbitrary arguments

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

print(tracer(func, 1, 2, c=3, d=4))


#废弃的apply内置函数(python2.6),不建议使用
func(*pargs, **kargs)             # Newer call syntax: func(*sequence, **dict) 新的调用语法

apply(func, pargs, kargs)         # Defunct built-in:  apply(func, sequence, dict) apply内置函数

#例子:
>>> def echo(*args, **kwargs): print(args, kwargs)
...
>>> echo(1, 2, a=3, b=4)
(1, 2) {'a': 3, 'b': 4}


>>> pargs = (1, 2)
>>> kargs = {'a':3, 'b':4}

>>> apply(echo, pargs, kargs) #在python2.6中的调用方式
(1, 2) {'a': 3, 'b': 4}

>>> echo(*pargs, **kargs)    # 新的调用语法
(1, 2) {'a': 3, 'b': 4}


>>> echo(0, c=5, *pargs, **kargs)      # Normal, keyword, *sequence, **dictionary
(0, 1, 2) {'a': 3, 'c': 5, 'b': 4}


#Python3 keyword-only参数
#python3把函数头部的排序规则通用化了,允许我们指点keyword-only参数,必须只按照关键字传递。
#如果想要一个函数既处理任意多个参数,也接受可能的配置选项的话,这是很有用的。
#keyworld-only参数编码为命名的参数,出现在参数列表中的*args之后,所有这些参数必须在调用中使用关键字语法来传递。

>>> def kwonly(a, *b, c):   #c必须按照关键字传递
...     print(a, b, c)
...
>>> kwonly(1, 2, c=3)
1 (2,) 3
>>> kwonly(a=1, c=3)
1 () 3
>>> kwonly(1, 2, 3)
TypeError: kwonly() needs keyword-only argument c


#可以在参数列表中使用一个*,表示这个函数不会接受一个可变长度的参数列表,而且期待跟在*后面的所有参数都作为关键字传递。
>>> def kwonly(a, *, b, c): #b,c都必须按照关键字传递
...     print(a, b, c)
...
>>> kwonly(1, c=3, b=2)
1 2 3
>>> kwonly(c=3, b=2, a=1)
1 2 3
>>> kwonly(1, 2, 3)
TypeError: kwonly() takes exactly 1 positional argument (3 given)
>>> kwonly(1)
TypeError: kwonly() needs keyword-only argument b


#带有默认值的keyword-only参数,b和c是可选的,但是如果使用的话必须按照关键字传递
>>> def kwonly(a, *, b='spam', c='ham'):
...     print(a, b, c)
...
>>> kwonly(1)
1 spam ham
>>> kwonly(1, c=3)
1 spam 3
>>> kwonly(a=1)
1 spam ham
>>> kwonly(c=3, b=2, a=1)
1 2 3
>>> kwonly(1, 2)
TypeError: kwonly() takes exactly 1 positional argument (2 given)


#b和c必须通过关键字参数传递,但是c有默认值
>>> def kwonly(a, *, b, c='spam'):
...     print(a, b, c)
...
>>> kwonly(1, b='eggs')
1 eggs spam
>>> kwonly(1, c='eggs')
TypeError: kwonly() needs keyword-only argument b
>>> kwonly(1, 2)
TypeError: kwonly() takes exactly 1 positional argument (2 given)

#b,c和d必须通过关键字参数传递,但是b和d有默认值
>>> def kwonly(a, *, b=1, c, d=2):
...     print(a, b, c, d)
...
>>> kwonly(3, c=4)
3 1 4 2
>>> kwonly(3, c=4, b=5)
3 5 4 2
>>> kwonly(3)
TypeError: kwonly() needs keyword-only argument c
>>> kwonly(1, 2, 3)
TypeError: kwonly() takes exactly 1 positional argument (3 given)


>>> def kwonly(a, **pargs, b, c):  #keyword-only参数必须跟在一个单个星号后面,而不是两个星号
SyntaxError: invalid syntax
>>> def kwonly(a, **, b, c):   #一个**不能独自出现在参数列表中
SyntaxError: invalid syntax
#所以,函数定义中,keyword-only参数必须编写在**args任意关键字形式之前,且在*args任意位置形式之后。


>>> def f(a, *b, **d, c=6): print(a, b, c, d)          # Keyword-only before **!
SyntaxError: invalid syntax

>>> def f(a, *b, c=6, **d): print(a, b, c, d)          # Collect args in header
...
>>> f(1, 2, 3, x=4, y=5)                               # Default used
1 (2, 3) 6 {'y': 5, 'x': 4}

>>> f(1, 2, 3, x=4, y=5, c=7)                          # Override default
1 (2, 3) 7 {'y': 5, 'x': 4}

>>> f(1, 2, 3, c=7, x=4, y=5)                          # Anywhere in keywords
1 (2, 3) 7 {'y': 5, 'x': 4}

>>> def f(a, c=6, *b, **d): print(a, b, c, d)          # c is not keyword-only!
...
>>> f(1, 2, 3, x=4)
1 (3,) 2 {'x': 4}


#实际上,在函数调用中,类似的排序规则也是成立的:当传递keywo-only参数的时候,它们必须出现在一个**args形式之前。
#keywor-only参数可以编写在*args之前或之后,并且可能包含在**args中
>>> def f(a, *b, c=6, **d): print(a, b, c, d)          # KW-only between * and **
...
>>> f(1, *(2, 3), **dict(x=4, y=5))                    # Unpack args at call
1 (2, 3) 6 {'y': 5, 'x': 4}

>>> f(1, *(2, 3), **dict(x=4, y=5), c=7)               # Keywords before **args!
SyntaxError: invalid syntax

>>> f(1, *(2, 3), c=7, **dict(x=4, y=5))               # Override default
1 (2, 3) 7 {'y': 5, 'x': 4}

>>> f(1, c=7, *(2, 3), **dict(x=4, y=5))               # After or before *
1 (2, 3) 7 {'y': 5, 'x': 4}

>>> f(1, *(2, 3), **dict(x=4, y=5, c=7))               # Keyword-only in **
1 (2, 3) 7 {'y': 5, 'x': 4}


#为什使用keywor-only参数,有时一个函数既接受任意多个要处理的位置参数,也接受作为关键字传递的配置选项
process(X, Y, Z)                    # use flag's default
process(X, Y, notify=True)          # override flag default

def process(*args, notify=False): ...    #解决方案


#求一个集合的最小值,3种版本
### file: mins.py

def min1(*args):
    res = args[0]
    for arg in args[1:]:
        if arg < res:
            res = arg
    return res

def min2(first, *rest):
    for arg in rest:
        if arg < first:
            first = arg
    return first

def min3(*args):
    tmp = list(args)            # Or, in Python 2.4+: return sorted(args)[0]
    tmp.sort()
    return tmp[0]

print(min1(3,4,1,2))
print(min2("bb", "aa"))
print(min3([2,2], [1,1], [3,3]))


#通用化单个函数求最大值或最小值
### file: minmax.py

def minmax(test, *args):
    res = args[0]
    for arg in args[1:]:
        if test(arg, res):
            res = arg
    return res

def lessthan(x, y): return x < y                # See also: lambda
def grtrthan(x, y): return x > y

print(minmax(lessthan, 4, 2, 1, 5, 6, 3))       # Self-test code
print(minmax(grtrthan, 4, 2, 1, 5, 6, 3))


#两个函数
### file: inter2.py

def intersect(*args): #求集合中公共部分
    res = []
    for x in args[0]:                  # Scan first sequence
        for other in args[1:]:         # For all other args
            if x not in other: break   # Item in each one?
        else:                          # No: break out of loop
            res.append(x)              # Yes: add items to end
    return res

def union(*args):  #求多个集合中的并集
    res = []
    for seq in args:                   # For all args
        for x in seq:                  # For all nodes
            if not x in res:
                res.append(x)          # Add new items to result
    return res

def intersect2(*args):
    res = []
    for x in args[0]:                  # Scan first sequence
        i = 0
        flag = False  #元素不在里面
        while i < len(args[1:]):
            if x not in args[1:][i]:
                break
            i += 1
        else:
            flag = True
        if flag == True: res.append(x)
    return res



>>> from inter2 import intersect, union
>>> s1, s2, s3 = "SPAM", "SCAM", "SLAM"

>>> intersect(s1, s2), union(s1, s2)           # Two operands
(['S', 'A', 'M'], ['S', 'P', 'A', 'M', 'C'])

>>> intersect([1,2,3], (1,4))                  # Mixed types
[1]

>>> intersect(s1, s2, s3)                      # Three operands
['S', 'A', 'M']

>>> union(s1, s2, s3)
['S', 'P', 'A', 'M', 'C', 'L']


# 模拟python3的print函数,这是一个例子
#如果在python2.X要使用python3的print函数,可以 from __future__ import print_function

### file: print30.py (first version)

"""
emulate most of the 3.0 print function for use in 2.X
call signature: print30(*args, sep=' ', end='\n', file=None)
"""
import sys

def print30(*args, **kargs):
    sep  = kargs.get('sep', ' ')             # Keyword arg defaults
    end  = kargs.get('end', '\n')
    file = kargs.get('file', sys.stdout)
    output = ''
    first  = True
    for arg in args:
        output += ('' if first else sep) + str(arg)
        first = False
    file.write(output + end)


#测试脚本,测试上面的print30函数
### file: testprint30.py

from print30 import print30
print30(1, 2, 3)
print30(1, 2, 3, sep='')                     # Suppress separator
print30(1, 2, 3, sep='...')
print30(1, [2], (3,), sep='...')             # Various object types

print30(4, 5, 6, sep='', end='')             # Suppress newline
print30(7, 8, 9)
print30()                                    # Add newline (or blank line)

import sys
print30(1, 2, 3, sep='??', end='.\n', file=sys.stderr)    # Redirect to file


#keyword-only版本的print30函数,并且都带有默认值
### file print30.py, modified

# Use keyword-only args
def print30(*args, sep=' ', end='\n', file=sys.stdout):
    output = ''
    first  = True
    for arg in args:
        output += ('' if first else sep) + str(arg)
        first = False
    file.write(output + end)



>>> print30(99, name='bob')
TypeError: print30() got an unexpected keyword argument 'name'


#最后一个版本
### file print30.py, modified

# Use keyword args deletion with defaults

def print30(*args, **kargs):
    sep  = kargs.pop('sep', ' ')
    end  = kargs.pop('end', '\n')
    file = kargs.pop('file', sys.stdout)
    if kargs: raise TypeError('extra keywords: %s' % kargs)
    output = ''
    first  = True
    for arg in args:
        output += ('' if first else sep) + str(arg)
        first = False
    file.write(output + end)



>>> print30(99, name='bob')
TypeError: extra keywords: {'name': 'bob'}

#keyword-only参数可以简化一类既接受参数又接受选项的函数。

#为什么要在意关键字参数:
#关键字参数在tkinter中扮演很重要的角色。
# the following are incomplete examples
# text和command关键字参数定义了文本和事件函数
from tkinter import *
widget = Button(text="Press me", command=someFunction)

#sorted内置函数
sorted(iterable, key=None, reverse=False)




#### quiz code


>>> def func(a, b=4, c=5):
...     print(a, b, c)
...
>>> func(1, 2)



>>> def func(a, b, c=5):
...     print(a, b, c)
...
>>> func(1, c=3, b=2)



>>> def func(a, *pargs):
...     print(a, pargs)
...
>>> func(1, 2, 3)



>>> def func(a, **kargs):
...     print(a, kargs)
...
>>> func(a=1, c=3, b=2)



>>> def func(a, b, c=3, d=4): print(a, b, c, d)
...
>>> func(1, *(5,6))












评论

此博客中的热门博文

OAuth 2教程

网格策略

apt-get详细使用