02-函数的参数

python学习网 2017-08-09 17:12:01

函数的参数

形参 & 实参

x和y其实就是变量名,在定义的阶段其实是不占用内容的。但是1和2是变量的值,它是要占用空间的,因此形参其实就是定义了一堆变量名,而实参则是实际的变量的值。其实就是相当于x=1y=2。x和y指向了1和2所在的内存地址(绑定关系)。这种绑定关系只有在调用的时候才会生效,调用结束后失效,这种绑定关系只在函数内部有效:

x = "我是外部的"
def test(x,y):
    print(x)
    print(y)

test("我是内部的",2)
print(x)

结果:
我是内部的
2
我是外部的

在调用的时候,实参的位置也可以放变量,不一定是放具体的值。当然你传一个列表,传一个字典……都是可以的。你传啥就是啥。实参的值要是不可变类型(数字、元组、字符串),虽然列表这种可变类型你也可以传,但是不可变类型传的就是值,可变类型传的是内存地址,生效范围就不是函数内部了,看下面的例子:

不可变类型:

def test(x):
    x = 3
x = 1
test(x)
print(x)

结果:1

可变类型:

def test(x):
    x.append(4)
x = [1,2,3]
test(x)
print(x)

结果:
[1,2,3,4]

因为可变类型传的是内存地址,因此改了就是直接去操作对应的内存地址的数据了,因此数据被影响了。因此特别注意,不要在函数中动全局变量,只改内部就够了,也就是传值的时候别传递可变类型。

位置参数 & 默认参数

实参的角度

  • 按位置传值
def test(x,y):
    print(x)
    print(y)
test(1,2)

1对应x,2对应y,从实参的角度,这叫按位置传值。
  • 按关键字传值
def test(x,y):
    print(x)
    print(y)
test(x=1,y=2)

按照关键字进行传值,你可以换位置,随便换都没事,因为这边按照关键字明确的指定了。
  • 位置传值+关键字传值混用
def test(x,y):
    print(x)
    print(y)
test(1,y=2)

这种情况下收到位置传值的限制,也就是说1的位置不能变,你把1和y=2的位置换一下就会报错了,因为y有指定值,但是1却没办法对应x了,报错信息如下:

SyntaxError: positional argument follows keyword argument

不能对一个形参多次赋值,不然会报错。也就是说你不管是用位置传参还是关键字传参,不能进行多次传值,不能重复传值。

形参的角度

  • 位置参数:必须传值的参数,如果你定义一个位置参数就必须给他传值。经常发生变化的就定义成位置参数。
def test(x,y):
    print(x)
    print(y)

不传值就会报错:

TypeError: test() missing 1 required positional argument: 'x'
  • 默认参数,不经常发生变化的就定义成默认参数。
def test(x,y="hahaha"):
    print(x)
    print(y)

test(1)
test(3,4)

结果:
1
hahaha
3
4

在默认参数的时候传值不是必须的,如果没有传值不会报错直接会使用默认的。上面默认的就是y=“hahaha”。默认参数必须放到位置参数的后面,不然会报错

再来看一个问题:

test1 = "test"

def test(x,y=test1):
    print(x)
    print(y)
test1 = "hey!!"

test(1)

结果:
1
test

可以发现test1 = “hey!!”并没有赋值进去,因为在def定义阶段,y=test1这个就已经加载到内存中了,也就是说y='test'就已经确定了。后续的赋值便不会再印象函数这里面的y了。

可变参数

  • *args(这只是一种约定俗称的写法,你可以用*a,或者*b)
def foo(x,*args):
    print(x)
    print(args)
foo(1,2,3,4,5,6,7,8)

结果:
1
(2, 3, 4, 5, 6, 7, 8)

*args会把按位置传参多余的部分已元组的形式存放到args里面,而不会报错。一般*args和默认参数不会放到一块去用。因此*args的作用就是接受无穷无尽的参数,之前我们再写可以接受参数的函数的时候会用def foo(x,y,z……),假如我需要接受的位置参数有很多,我们就可以使用*args来替代,简单来说我们就可以用它来带位置参数。

def foo(*args):
    xxxxx
    
同样的在调用的时候实参也可以使用*的形式,比如:

def foo(x,y,z):
    print(x)
    print(y)
    print(z)
    
foo(*(1,2,3)) ==等同于== foo(1,2,3)
相当于将这个(1,2,3)的元组给打散了去传值。
  • **args

比*args多了个星号,区别就在于一个*会把多余的元素转换成元组,但是**args的情况会把多余的元素转换成字典,因为是字典肯定就是键值对,要写成下面的格式:

def foo(x,**args):
    print(x)
    print(args)
foo(1,a=2,b=3,c=4,d=5,e=6,f=7,g=8)

结果:
1
{'a': 2, 'b': 3, 'c': 4, 'd': 5, 'e': 6, 'f': 7, 'g': 8}

如果调用foo不是一键值对的形式传值的话会报错,会报问题说我只要一个位置参数你为啥给我那么多。

总结一下

  1. *args就是按照位置参数传值,如果多余的就给*args
  2. **args是关键字传值,也就是a=1这种的,多余的会被**args接收。
def foo(*args,**kwargs)
这种形式就可以接受各种类型的了各种值了。

但是注意两点(实参的角度)
1--不能重复传值
2--关键字传值要在位置传值的后面

那么*可以用在实参的位置,打散了去看,同样的**也是一样,打散了是可以的,但是注意关键字要对应,比如形参是x,你别给传一个a=xx就行:

def foo(x,y,z):
    print(x,y,z)

foo(**{"x":1,"y":2,"z":3})

结果:
1 2 3
阅读(809) 评论(0)