python学习笔记一

函数

声明

def name([arg,... arg = value,... *arg, **kwarg]):
suite

包括函数在内的所有对象都是第⼀类对象,可作为其他函数的实参或返回值。
• 在名字空间中,名字是唯⼀主键。因此函数在同⼀范围内不能 "重载 (overload)"。
• 函数总是有返回值。就算没有 return,默认也会返回 None。
• 支持递归调⽤,但不进⾏尾递归优化。最⼤深度 sys.getrecursionlimit()。
不同于⽤ def 定义复杂函数,lambda 只能是有返回值的简单的表达式。使⽤用赋值语句会引发语法错误,可以考虑用函数代替。


参数

<span style="font-size:18px;">>>> def test(a, b):
... print a, b
>>> test(1, "a")! ! ! # 位置参数
1 a
>>> test(b = "x", a = 100)!! # 命名参数
100 x</span>

函数的传参方式灵活多变,可按位置顺序传参,也可不关心顺序用命名实参。
⽀持参数默认值。不过要⼩心,默认值对象在创建函数时⽣生成,所有调⽤都使⽤同一对象。如果该默认值是可变类型,那么就如同 C 静态局部变量。
用 *args 收集 "多余" 的位置参数,**kwargs 收集 "额外" 的命名参数。这两个名字只是惯例,可自由命名。
变参只能放在所有参数定义的尾部,且 **kwargs 必须是最后⼀个。
可 "展开" 序列类型和字典,将全部元素当做多个实参使用。如不展开的话,那仅是单个实参对象。
单个 "*" 展开序列类型,或者仅是字典的主键列表。"**" 展开字典键值对。但如果没有变参收集,展开后多余的参数将引发异常。
lambda 同样支持默认值和变参,使用方法完全一致。


作用域

函数形参和内部变量都存储在locals名字空间中。
除⾮使用 global、nonlocal 特别声明,否则在函数内部使⽤赋值语句,总是在 locals 名字空间中新建⼀个对象关联。注意:"赋值" 是指名字指向新的对象,⽽非通过名字改变对象状态。不改变外部global的对象,只是在函数内创建了一个新的对象。
如果仅仅是引用外部变量,那么按LEGB顺序在不同作用域查找该名字。
名字查找顺序: locals -> enclosing function -> globals -> __builtins__
• locals: 函数内部名字空间,包括局部变量和形参。
• enclosing function: 外部嵌套函数的名字空间。
• globals: 函数定义所在模块的名字空间。
• __builtins__: 内置模块的名字空间。
通常内置模块 __builtin__ 在本地名字空间的名字是 __builtins__ (多了个 s 结尾)。但要记住这说法⼀点也不靠谱,某些时候它⼜会莫名其妙地指向 __builtin__.__dict__。如实在要操作该模块,建议显式 import __builtin__。
现在,获取外部空间的名字没问题了,但如果想将外部名字关联到⼀个新对象,就需要使⽤用 global关键字,指明要修改的是 globals 名字空间。Python 3 还提供了 nonlocal 关键字,⽤用来修改外部嵌套函数名字空间,可惜 2.7 没有
这种实现通过 _getframe() 来获取外部函数堆栈帧名字空间,存在一些限制。因为拿到是调⽤者,而不⼀定是函数创建者。需要注意,名字作⽤用域是在编译时确定的。
要解决这个问题,可动态访问名字,或使⽤ exec 语句,解释器会做动态化处理。如果函数中包含 exec 语句,编译器生成的名字指令会依照 LEGB 规则搜索
解释器会将 locals 名字复制到 FAST 区域来优化访问速度,因此直接修改 locals 名字空间并不会影响该区域。解决⽅法还是用 exec。
另外,编译期作⽤域不受执行期条件影响。其中细节,可以用dis反编译查看生成的字节指令。


堆栈帧
Python堆栈帧基本上就是对x86的模拟,用指针对应BP,SP,IP寄存器。堆栈帧尘缘包括函数执行所需的名字空间、调用堆栈链表、异常状态等。
可使⽤ sys._getframe(0) 或 inspect.currentframe() 获取当前堆栈帧。其中 _getframe() 深度参数为 0 表⽰示当前函数,1 表⽰调⽤堆栈的上个函数。
权限管理:通过调用堆栈检查函数Caller,以实现权限管理。
上下文:通过调用堆栈,我们可以隐式向整个执行流程传递上下文对象。inspect.stack 比 frame.f_back更方便点。sys._current_frames 返回所有线程的当前堆栈帧对象。虚拟机会缓存 200 个堆栈帧复⽤对象,以获得更好的执⾏性能。
<span style="font-size:18px;">>>> import inspect
>>> def get_context():
... for f in inspect.stack():! ! ! # 循环调⽤用堆栈列表。
... context = f[0].f_locals.get("context")! # 查看该堆栈帧名字空间中是否有⺫⽬目标。
... if context: return context! ! ! # 找到了就返回,并终⽌止查找循环。
>>> def controller():
... context = "ContextObject"! ! ! # 将 context 添加到 locals 名字空间。
... model()
>>> def model():
... print get_context()! ! ! ! # 通过调⽤用堆栈查找 context。
>>> controller()! ! ! ! ! ! # 测试通过。
ContextObject</span>



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值