设为首页 收藏本站
查看: 954|回复: 0

[经验分享] Python札记

[复制链接]

尚未签到

发表于 2015-4-19 07:47:27 | 显示全部楼层 |阅读模式
  在上一篇随笔《Python凡人笔记 -- 装饰器》中有园友提出我对Python中的参数魔法表述得不是很明确,故借此机会总结一下,再结合几个例子,希望对大家有帮助。另外竹风使用的是 Python 2.6.6
  参考资料:《Python基础教程》(第2版)Magnus Lie Hetland
  一、Python的函数返回些什么?
    一般说的函数,总会在计算之后返回点什么。像在 Pascal 里面,就有分函数和过程。让我们一起从例子里面看看 Python 的函数到底会返回些什么。



1 def test():
2     print "This is printed"
3     return
4     print "This is not"
  这里的return语句只起到结束函数的作用:



1 >>> x = test()
2 This is printed
3
4 >>> x
5 >>>
6
7 >>> print x
8 None
  可以看到,第二个print语句被跳过了。单单看 x 好像没有任何东西,通过 “print x” 我们可以看到一个熟悉的值:None。
    这么看来,在Python里面,所有的函数都会返回值;当不需要它们返回值的时候,它们就返回None。
  二、什么类型的参数可以修改?
    函数通过参数来获得一系列的值。在Python中,字符串(以及数字和元组)是不可变的,即无法被修改(也就是说只能用新的值覆盖),用学术点的话来说:在函数内为不可变类型的参数赋予新值不会改变外部任何变量的值。   



1 >>> def try_to_change(n):
2 ...     n = "Mr Gumby"
3 ...
4 >>> name = "Mrs Entity"
5 >>> try_to_change(name)
6 >>> name
7 'Mrs Entity'
  我们可以看到,name 虽然作为参数传递到函数中,但是最后 name 的值并没有任何改变。其实上面的代码工作方式类似于下面这样:



1 >>>name = "Mrs Entity"
2 >>>n = name    #相当于传参数
3 >>>n = "Mr Gumby"    #在函数内部给n一个新的字符串
4 >>>name
5 'Mrs Entity'
  那么如果我们传的参数是一个可变的数据结构呢,比如列表?估计大家已经能猜到结果了。



1 >>> def change(n):
2 ...     n[0] = "Mr Gumby"
3 ...
4 >>> name = ["Mrs Entity", "Mrs Thing"]
5 >>> change(name)
6 >>> name
7 ['Mr Gumby', 'Mrs Thing']
  这里发现参数的值改变了,这也是与前面的例子中最重要的区别。要弄清楚的是,当两个变量同时引用一个列表的时候,它们的确是同时引用了一个列表。如果想避免出现这种情况,可以复制一个列表的副本。具体做法就是传递参数的时候,将列表的分片作为参数,比如:change(name[:])。这样的话,原始的列表就是安全的。
    PS:竹风刚入门的时候曾经犯过将原始列表传入函数的错误,关键是函数里面有一个 remove 操作,结果大家也能猜到了。。。
  那如果要对一个不可变的参数进行操作呢?比如一个数字,这又该怎么办?
    这个时候我们应该从函数中返回所有需要的值,如果值多于一个拿就用元组的形式返回。例如,将变量的数值增加1的函数可以这么写:



1 >>> def inc(x):return x + 1
2 ...
3 >>> foo = 10
4 >>> foo = inc(foo)
5 >>> foo
6 11
  如果真的想改变参数的话,我们可以使用一点小技巧,将值放在列表中(还记得列表是可修改的么?):



1 >>> def inc(x): x[0] = x[0] + 1
2 ...
3 >>> foo = [10]
4 >>> inc(foo)
5 >>> foo
6 [11]
  使用哪种方式,全凭个人喜好~~
  三、位置参数,关键字参数,默认参数
    到目前为止,前面例子中使用的都是位置参数,因为它们的位置很重要——事实上比它们的名字更重要。先看些简单的列子,来看看为什么会引入关键字参数。



1 >>> def hello_1(greeting, name):
2 ...     print "%s, %s!" % (greeting, name)
3 ...
4 >>> def hello_2(name, greeting):
5 ...     print "%s, %s!" % (name, greeting)
6 ...
7 >>> hello_1("Hello", "World")
8 Hello, World!
9 >>> hello_2("World", "Hello")
10 World, Hello!
  这两个函数所实现的功能是完全一样的,唯一的区别就是参数的顺序。当参数很多的时候,竹风觉得参数的顺序是很难记住的。为了让事情简单些,我们可以提供参数的名字。



1 >>> hello_1(greeting = "Hello", name = "World")
2 Hello, World!
3 >>> hello_1(name = "World", greeting = "Hello")
4 Hello, World!
5 >>> hello_2(greeting = "Hello", name = "World")
6 World, Hello!
  提供了参数的名字后,顺序就无关紧要的,就好像上面的 hello_1 调用。唯一要注意的,就是参数名和值一定要对应,不然就会出现 hello_2 那样输出 “World Hello!”了。
    这类使用参数名提供的参数叫做关键字参数。主要作用在于可以明确每个参数的作用,也就避免了下面这样奇怪的调用:



1 >>>stroe("Mr Brainsample", 10, 20, 13, 5)
2
3 >>>stroe(patient="Mr Brainsample", hour=10, minute=20, day=13, month=5)
  竹风以为,大部分园友看见第一种调用的时候估计是虎躯一震的。。。第二种调用虽然打的字多了点,但是参数的含义变得更加清晰,而且就算弄乱了参数的顺序,对于程序的功能也没有任何影响。
    关键字参数最实用的地方在于可以在函数中给参数提供默认值,嘎嘎,再也不用担心调用函数的时候漏参数了~~   



>>> def hello_3(greeting="Hello", name="World"):
...     print "%s, %s!" % (greeting, name)
...
>>> hello_3()    #使用默认参数
Hello, World!
>>> hello_3('Greetings')    #使用位置参数将值传给greeting
Greetings, World!
>>> hello_3('Greetings','God')    #同时使用位置参数
Greetings, God!
>>> hello_3('Greetings',name = 'Tom')    #位置参数和关键字参数混用
Greetings, Tom!
>>> hello_3(name = 'Jerry')    #只使用关键字参数指定name
Hello, Jerry!
  最后关于位置参数和关键字参数需要注意的是:同时使用位置和关键字参数的时候,必须把位置参数放置在关键字参数的前面,否则,解释器就不知道这些参数到底应该处在什么位置了。
  四、收集参数与解开参数
    铺垫了这么久,终于来到今天的两大Boss面前了。位置参数拉好仇恨,关键字参数做好治疗,其他人全力输出吧!(博主wow五人本打多了。。。无视就好。。。)
    先说说收集参数,在定义函数时,*params 收集其余的位置参数(parameter),返回元组;**kwparams 收集其余的关键字参数(key word parameter),返回字典。
    有时候能提供任意数量的参数是相当方便的,比如一个函数 add 可以求任意个数字的和,add(1,2,3) 和 add(1,2,3,4,5) 都能给出正确结果。
    用户可以给函数提供任意多的参数,实现起来也不难。注意例子中虽然只提供了一个参数,但是前面加上了个星号。   



1 >>> def print_params(*params):
2 ...     print params
3 ...
4 >>> print_params('Testing')
5 ('Testing',)
6 >>> print_params('Testing',1,2,3)
7 ('Testing', 1, 2, 3)
  可以看到,结果作为元组打印出来。参数前的星号将所有传进来的参数放置在同一个元组中。也可以说是把这些值收集起来,然后使用。当然也可以联合普通参数来使用:



1 >>> def print_params_2(title, *params):
2 ...     print "title =",title
3 ...     print params
4 ...
5 >>> print_params_2("Test2", 1, 2, 3)
6 title = Test2
7 (1, 2, 3)
8 >>> print_params_2("Test3")
9 title = Test3
10 ()
  完全没问题!所以星号的意思就是“收集其余的位置参数”。如果不提供任何收集的元素,params就是个空元组。
    让我们看下关键字参数的“收集”操作。



1 >>> def print_params_3(**kwparams):
2 ...     print kwparams
3 ...
4 >>> print_params_3(x=1, y=2, z=3)
5 {'y': 2, 'x': 1, 'z': 3}
  返回的是字典而不是元组。将它们放在一起看看:



>>> def print_params_4(x, y, z=3, *pospar, **keypar):
...     print x, y, z
...     print pospar
...     print keypar
...
>>> print_params_4(1, 2, 3, 5, 6, 7, foo='A', bar='B')
1 2 3
(5, 6, 7)
{'foo': 'A', 'bar': 'B'}
>>> print_params_4(1, 2, 3)
1 2 3
()
{}
  ok,跟我们期望的结果没有差别。
  收集参数打完了,再来打展开参数。
    展开参数,在调用函数时,*params 将元组拆分为位置参数(parameter)传入;**kwparams 将字典拆分为关键字参数(key word parameter)传入。
    这是两个典型的例子:   



1 >>> def add(x,y): return x + y
2 ...
3 >>> params = (1, 2)
4 >>> add(*params)
5 3
6 >>> def hello_3(greeting="Hello", name="World"):
7 ...     print "%s, %s!" % (greeting, name)
8 ...
9 >>> kwparams = {'name': 'Sir Robin', 'greeting': 'Well met'}
10 >>> hello_3(**kwparams)
11 Well met, Sir Robin!
  最后我们看看这几种方式一起使用的情况:



1 >>> def finally_test(x, y, z=3, *params, **kwparams):    #参数的顺序应该为:位置参数,关键字参数,*params,**kwparams
2 ...     print "pos x =",x
3 ...     print "pos y =",y
4 ...     print "key z =",z
5 ...     print "params =",params
6 ...     print "kwparams =",kwparams
7 ...
8 >>> list = [1,2,3,4,5]
9 >>> dict = {"foo":"A", "bar":"B"}
10
11 >>> finally_test(1,2,*list,**dict)    #没有指定z的值,所以将list[0]作为z的值,其他收集为params
12 pos x = 1
13 pos y = 2
14 key z = 1
15 params = (2, 3, 4, 5)
16 kwparams = {'foo': 'A', 'bar': 'B'}
17
18 >>> finally_test(1,2,3,list,dict)    #错误的调用方法,会将dict也认为是个位置参数
19 pos x = 1
20 pos y = 2
21 key z = 3
22 params = ([1, 2, 3, 4, 5], {'foo': 'A', 'bar': 'B'})
23 kwparams = {}
24
25 >>> finally_test(1,2)    #没有多余的位置参数和关键字参数的情况
26 pos x = 1
27 pos y = 2
28 key z = 3
29 params = ()
30 kwparams = {}
31
32 >>> finally_test(1,2,3,*list,**dict)    #正确的调用方式
33 pos x = 1
34 pos y = 2
35 key z = 3
36 params = (1, 2, 3, 4, 5)
37 kwparams = {'foo': 'A', 'bar': 'B'}
  其实一般情况下会这么定义函数:
    def foo(*params,**kwparams):
        pass
  到这里就总结完了,希望对大家有用。如有不足,欢迎大家指正交流,谢谢^_^

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-58544-1-1.html 上篇帖子: 【循序渐进学Python】2. Python中的序列——列表和元组 下篇帖子: [Python] Python 调用 C 共享库
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表