绑定完请刷新页面
取消
刷新

分享好友

×
取消 复制
Python White Magic:重新绑定全局变量
2019-12-12 11:19:09

https://speakerdeck.com/u/antocuni/p/python-white-magic

本文的参考资料

扩展pdb模块时遇到的问题

在Python的pdb模块中有如下代码:

class Pdb(bdb.Bdb, cmd.Cmd):

...

def runcall(*args, **kwds):

return Pdb().runcall(*args, **kwds)

def set_trace():

Pdb().set_trace(sys._getframe().f_back)

为了方便使用Pdb类,pdb模块提供了诸如runcall()、set_trace()等便捷函数。在这些函数内部创建Pdb对象,并调用其对应的方法。现在我们需要继承Pdb类,为其提供更多的功能,于是在”pdbpp.py”模块中:

import pdb

class Pdb(pdb.Pdb):

...

通过继承pdb.Pdb,我们可以复用pdb模块中Pdb类所提供的代码,然而我们没有很好的办法复用pdb模块中的set_trace()等便捷函数。因为这些函数中的Pdb参照的是pdb模块中的Pdb类。为了复用这些函数,我们需要重新绑定其全局变量。假设rebind_globals()能重新绑定函数的全局变量,按么我们可以在”pdbpp.py”中如此复用便捷函数:

set_trace = rebind_globals(pdb.set_trace)

rebind_globals()将pdb.set_trace函数中所参照的全局变量改为当前模块中的全局变量。

rebind_globals()的实现

下面我们先看看rebind_globals()的完整代码。

import *

def rebind_globals(func, newglobals=None):

if newglobals is None:

newglobals = globals() ❶

newfunc = *.FunctionType(func.func_code, ❷

 newglobals,

 func.func_name,

 func.func_defaults)

return newfunc

rebind_globals()有一个可选参数newglobals,❶当它为None时将使用globals()所获得的全局变量字典作为函数func的新的全局变量。此外也可以直接传入一个字典给newglobals。

❷使用func的一些属性和新的全局变量字典创建一个新的函数对象,并返回它。下面我们用一个简单的例子演示rebind_globals()的功能。

>>> a = 10

>>> def f():

... print a

>>> f2 = rebind_globals(f, {"a":"changed"})

>>> f()

10

>>> f2()

changed

要完全理解这段代码的工作原理需要理解function对象。

function和code对象

在Python中函数也是对象,它有几个比较重要的属性:

func_globals: 函数所参照的全局变量字典,通常就是定义函数时通过globals()获得的字典。

func_code: 表示函数字节码的代码对象。

func_name: 定义函数时的函数名。

func_defaults: 保持函数缺省值的元组。

下面我们看一个例子:

>>> def f(x=1, y=2):

... return x+y

...

>>> f.func_defaults

(1, 2)

>>> f.func_name

'f'

>>> f.func_code

", line 1>

>>> f.func_globals is globals()

True

下图显示了函数对象、代码对象以及全局变量字典之间的关系:

图中,函数对象参照一个全局变量字典以及一个代码对象,代码对象中的字节码通过变量名在全局字典中查找其对应的值。显然只需要修改函数对象的func_globals属性,就能让函数在完全不同的全局变量环境之下运行。但是很遗憾,func_globals是只读属性,我们无法修改它。于是我们需要使用*模块中提供的FunctionType动态创建函数。

*模块

*模块中定义了Python所有的内置类型,例如FunctionType为函数类型:

>>> from * import FunctionType

>>> isinstance(f, FunctionType)

True

>>> FunctionType

>>> type(f)

通过FunctionType可以动态创建函数,查看其帮助:

>>> help(FunctionType)

Help on class function in module __builtin__:

class function(object)

| function(code, globals[, name[, argdefs[, closure]]])

|

| Create a function object from a code object and a dictionary.

| The optional name string overrides the name from the code object.

| The optional argdefs tuple specifies the default argument values.

| The optional closure tuple supplies the bindings for free variables.

...

它的参数分别为:

code: 代码对象

globals: 全局变量字典

name: 函数名

argdefs: 缺省值

closure: 这个属性只在嵌套函数中有用

在rebind_globals()中我们使用FunctionType创建了一个新的函数对象,除了globals之外,其它的参数全部使用原函数的属性。这样就创建了一个新的函数对象,复用了原函数的代码对象。其效果如下图所示:

分享好友

分享这个小栈给你的朋友们,一起进步吧。

星说
创建时间:2019-11-28 12:45:43
作为炎黄子孙,我们有很多知识渊博的祖人,这个祖人指的是在各领域登峰造极的学者论说,我们跟随强大基因的路线,学习,深化自己的知识体系,优化环境。
展开
订阅须知

• 所有用户可根据关注领域订阅专区或所有专区

• 付费订阅:虚拟交易,一经交易不退款;若特殊情况,可3日内客服咨询

• 专区发布评论属默认订阅所评论专区(除付费小栈外)

栈主、嘉宾

查看更多
  • unnamed persona
    栈主

小栈成员

查看更多
  • 外星人6
  • supergirlxu
  • unnamed person1
  • daxuesheng
戳我,来吐槽~