Python:修改调用范围

时间:2017-09-20 07:33:27

标签: python parsing arguments parameter-passing introspection

是否可以使用真实代码替换下面的foo函数

def foo():
    #calling_scope()['a']=2
def bar(a):
    print(a)
    foo()
    print(a)
bar(1)

打印

1
2

为了避免XY问题,我真正想要的是让foo成为总结常见解析代码的外部模块的一部分。目前,我解决了如下解析:我有一个模块parse_args,其中包含一堆函数,例如

def bool(arg):
  if arg in [True,'True','true']:
     return True
  if arg in [False,'False','false']:
     return False
  raise ValueError('Did not understand boolean value')
def nonnegative(arg):
  if arg<0:
    raise ValueError('Argument cannot be negative')
  return arg  

我在bar中执行以下操作:

def bar(arg1,arg2)
    arg1=parse_args.bool(arg1)
    arg2=parse_args.nonnegative(arg2)

我希望能够做的是:

def bar(arg1,arg2)
    parse_args(arg1='bool',arg2='nonnegative')

parse_args的伪代码是

def parse_args(**kwargs)
    #cs=calling_scope()
    #for arg in kwargs:
    #    cs[arg]=globals()[kwargs[arg]](cs[arg])

我知道这只是稍微不那么罗嗦了,我知道有可能有理由支持我目前的方法而不是我的目标,但是作为一个仍在学习Python的人,我真的主要对这里的可行性感兴趣。

2 个答案:

答案 0 :(得分:2)

不,这是不可能的。您无法从堆栈的更下方更改函数的本地命名空间,因为CPython实现已经高度优化了本地命名空间,从而无法进行任何操作。

你以错误的方式解决问题。而不是依赖于标准的本地命名空间,而是以字典的形式创建自己的命名空间。然后你可以传递那个词典:

def bar(arg1, arg2)
    namespace = {'arg1': arg1, 'arg2': arg2}
    parse_args(namespace, arg1='bool', arg2='nonnegative')

答案 1 :(得分:0)

我找到了一种方法,使用函数注释和装饰器以比我希望的更漂亮的方式进行我想要的解析。 (实际上这实际上修改了我希望它们被修改的函数中的变量绑定,从技术上来说这是可能的,因为通过使用装饰器,调用者成为被调用者。)

我现在写

@validated()
def f(a:'nonnegative integer',b:'bool|function', c:'float in_range(0,1)', d:'{float}', e:'{integer:any}',f:'bool'):  
    print(a,b,c,d,e,f)

并获取

>>f(a=2,b=lambda x: x>2, c=0,d=[1.2, 2.3], e={1:'foo',2:'bar'},f='true')
2 <function <lambda> at 0x7f4779ae8400> 0.0 [1.2, 2.3] {1: 'foo', 2: 'bar'} True

请注意,整数参数c=0已转换为float 0.0,字符串'true'已转换为bool True。注释中的大括号表示任何可迭代,冒号表示字典。竖条表示备选方案。可以通过空格连接不同的要求,如参数ac所做的那样。如果不满足任何规范,例如a=-1,则会抛出有根据的错误:

swutil.validation.ValidationError: Invalid argument for parameter 'a': -1 was rejected by 'nonnegative integer' (-1 was rejected by 'nonnegative')

最后,装饰器接受可用于指定必须传递哪些参数的参数。例如,@validated('a^b')@validated('a|b')指定必须(确切地)传递ab中的一个。另一个将填充NotPassed实例。

如果有人对代码感兴趣,请发表评论并与我分享。