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

[经验分享] Python函数小结(1)

[复制链接]

尚未签到

发表于 2015-11-29 09:18:16 | 显示全部楼层 |阅读模式
  刚开始学习python,python相对于java确实要简洁易用得多。内存回收类似hotspot的可达性分析, 不可变对象也如同java得Integer类型,with函数类似新版本C++的特性,总体来说理解起来比较轻松。只是函数部分参数的"*"与"**",闭包等问题,着实令人迷糊了一把,弄清概念后写下此文记录下来,也希望本文能够帮助其他初学者。
  所以本文是一篇学习笔记,着重于使用的细节和理解上,首先分别介绍了函数各种参数类型在调用和声明时的区别,及其在混用时需要注意的一些细节,之后讲了闭包相关的内容。如果有不对的地方欢迎指正。

函数参数不带“*”,"*" 与 "**"的区别
  理解这个问题得关键在于要分开理解调用和声明语法中3者得区别.

函数调用区别
1. 不同类型的参数简述
  

  

  

#这里先说明python函数调用得语法为:  
func(positional_args, keyword_args,
  *tuple_grp_nonkw_args, **dict_grp_kw_args)
  
  #为了方便说明,之后用以下函数进行举例
  def test(a,b,c,d,e):
  print a,b,c,d,e
  

  

  举个例子来说明这4种调用方式得区别:
  

#-------------------------------  
#positional_args方式
  
>>> test(1,2,3,4,5)
  
1 2 3 4 5
  

  
#这种调用方式的函数处理等价于
  
a,b,c,d,e = 1,2,3,4,5
  
print a,b,c,d,e
  

  
#-------------------------------
  
#keyword_args方式
  
>>> test(a=1,b=3,c=4,d=2,e=1)
  
1 3 4 2 1
  

  
#这种处理方式得函数处理等价于
  
a=1
  
b=3
  
c=4
  
d=2
  
e=1
  
print a,b,c,d,e
  

  
#-------------------------------
  
#*tuple_grp_nonkw_args方式
  
>>> x = 1,2,3,4,5
  
>>> test(*x)
  
1 2 3 4 5
  

  
#这种方式函数处理等价于
  
a,b,c,d,e = x
  
print a,b,c,d,e
  
#特别说明:x也可以为dict类型,x为dick类型时将键传递给函数
  
>>> y
  
{'a': 1, 'c': 6, 'b': 2, 'e': 1, 'd': 1}
  
>>> test(*y)
  
a c b e d
  

  
#---------------------------------
  
#**dict_grp_kw_args方式
  
>>> y
  
{'a': 1, 'c': 6, 'b': 2, 'e': 1, 'd': 1}
  
>>> test(**y)
  
1 2 6 1 1
  

  
#这种函数处理方式等价于
  
a = y['a']
  
b = y['b']
  
... #c,d,e不再赘述
  
print a,b,c,d,e
  

  

2. 不同类型参数混用需要注意的一些细节
  接下来说明不同参数类型混用的情况,要理解不同参数混用得语法需要理解以下几方面内容.
  首先要明白,函数调用使用参数类型必须严格按照顺序,不能随意调换顺序,否则会报错. 如 (a=1,2,3,4,5)会引发错误,; (*x,2,3)也会被当成非法.
  其次,函数对不同方式处理的顺序也是按照上述的类型顺序.因为#keyword_args方式和**dict_grp_kw_args方式对参数一一指定,所以无所谓顺序.所以只需要考虑顺序赋值(positional_args)和列表赋值(*tuple_grp_nonkw_args)的顺序.因此,可以简单理解为只有#positional_args方式,#*tuple_grp_nonkw_args方式有逻辑先后顺序的.
  最后,参数是不允许多次赋值的.
  举个例子说明,顺序赋值(positional_args)和列表赋值(*tuple_grp_nonkw_args)的逻辑先后关系:
  

#只有在顺序赋值,列表赋值在结果上存在罗辑先后关系  
#正确的例子1
  
>>> x = {3,4,5}
  
>>> test(1,2,*x)
  
1 2 3 4 5
  
#正确的例子2
  
>>> test(1,e=2,*x)
  
1 3 4 5 2
  

  
#错误的例子
  
>>> test(1,b=2,*x)
  
Traceback (most recent call last):
  File "", line 1, in
  
TypeError: test() got multiple values for keyword argument 'b'
  

  
#正确的例子1,处理等价于
  
a,b = 1,2 #顺序参数
  
c,d,e = x #列表参数
  
print a,b,c,d,e
  

  
#正确的例子2,处理等价于
  
a = 1 #顺序参数
  
e = 2 #关键字参数
  
b,c,d = x #列表参数
  

  
#错误的例子,处理等价于
  
a = 1 #顺序参数
  
b = 2 #关键字参数
  
b,c,d = x #列表参数
  
#这里由于b多次赋值导致异常,可见只有顺序参数和列表参数存在罗辑先后关系
  

  

  

函数声明区别
  理解了函数调用中不同类型参数得区别之后,再来理解函数声明中不同参数得区别就简单很多了.
  
1. 函数声明中的参数类型说明
  函数声明只有3种类型, arg, *arg , **arg 他们得作用和函数调用刚好相反. 调用时*tuple_grp_nonkw_args将列表转换为顺序参数,而声明中的*arg的作用是将顺序赋值(positional_args)转换为列表. 调用时**dict_grp_kw_args将字典转换为关键字参数,而声明中**arg则反过来将关键字参数(keyword_args)转换为字典.
  特别提醒:*arg 和 **arg可以为空值.
  以下举例说明上述规则:
  

#arg, *arg和**arg作用举例  
def test2(a,*b,**c):
  print a,b,c
  
#---------------------------
  
#*arg 和 **arg可以不传递参数
  
>>> test2(1)
  
1 () {}
  
#arg必须传递参数
  
>>> test2()
  
Traceback (most recent call last):
  File "", line 1, in
  
TypeError: test2() takes at least 1 argument (0 given)
  

  
#----------------------------
  
#*arg将顺positional_args转换为列表
  
>>> test2(1,2,[1,2],{'a':1,'b':2})
  
1 (2, [1, 2], {'a': 1, 'b': 2}) {}
  
#该处理等价于
  
a = 1 #arg参数处理
  
b = 2,[1,2],{'a':1,'b':2} #*arg参数处理
  
c = dict() #**arg参数处理
  
print a,b,c
  

  
#-----------------------------
  
#**arg将keyword_args转换为字典
  
>>> test2(1,2,3,d={1:2,3:4}, c=12, b=1)
  
1 (2, 3) {'c': 12, 'b': 1, 'd': {1: 2, 3: 4}}
  
#该处理等价于
  
a = 1 #arg参数处理
  
b= 2,3 #*arg参数处理
  
#**arg参数处理
  
c = dict()
  
c['d'] = {1:2, 3:4}
  
c['c'] = 12
  
c['b'] = 1
  
print a,b,c
  

  
2. 处理顺序问题
  函数总是先处理arg类型参数,再处理*arg和**arg类型的参数. 因为*arg和**arg针对的调用参数类型不同,所以不需要考虑他们得顺序.
  

def test2(a,*b,**c):  print a,b,c
  
>>> test2(1, b=[1,2,3], c={1:2, 3:4},a=1)
  
Traceback (most recent call last):
  File "", line 1, in
  
TypeError: test2() got multiple values for keyword argument 'a'
  
#这里会报错得原因是,总是先处理arg类型得参数
  
#该函数调用等价于
  
#处理arg类型参数:
  
a = 1
  
a = 1  #多次赋值,导致异常
  
#处理其他类型参数
  
...
  
print a,b,c
  

  

闭包
  python的函数,原本只能访问两个区域的变量:全局,和局部(函数上下文). 实际上,函数本身也是一个对象,也有自己的作用域. 闭包通过函数与引用集合的组合,使得函数可以在它被定义的区域之外执行. 这个集合可以通过func_closure来获取这个引用集合. 这与python处理全局变量得方式一样,只不过全局变量将引用集合存储在__globals__字段中.func_closure是一个存储cell类型的元组,每个cell存储一个上下文变量.
  另外,旧版本得python的内部函数不能在其他作用域使用的原因,并不是因为每个作用域的变量严格相互隔离,而是脱离原本的作用域后,函数失去了原本上下文的引用。需要注意的是,闭包存储的上下文信息一样是浅拷贝,所以传递给内部函数的可变对象仍然会被其他拥有该对象引用得变量修改.
  举个例子:
  

>>> def foo(x,y):  
...     def bar():
  
...             print x,y
  
...     return bar
  
...
  
#查看func_closure的引用信息
  
>>> a = [1,2]
  
>>> b = foo(a,0)
  
>>> b.func_closure[0].cell_contents
  
[1, 2]
  
>>> b.func_closure[1].cell_contents
  
0
  
>>> b()
  
[1, 2] 0
  

  
#可变对象仍然能被修改
  
>>> a.append(3)
  
>>> b.func_closure[0].cell_contents
  
[1, 2, 3]
  
>>> b()
  
[1, 2, 3] 0
  

运维网声明 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-144794-1-1.html 上篇帖子: Python实现控制台中的进度条 下篇帖子: Python遍历文件夹和读写文件的方法
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

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

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

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

扫描微信二维码查看详情

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


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


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


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



合作伙伴: 青云cloud

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