【问题标题】:Dynamically adding key-arguments to method为方法动态添加关键参数
【发布时间】:2013-04-26 14:50:25
【问题描述】:

我想动态设置实例方法的默认键参数。例如,与

class Module(object):
    def __init__(self, **kargs):
        set-default-key-args-of-method(self.run, kargs)  # change run arguments
    def run(self, **kargs):
        print kargs

我们会:

m = Module(ans=42)

m.run.im_func.func_code.co_argcount  # => 2
m.run.im_func.func_code.co_varnames  # => ('self','ans','kargs')
m.run.im_func.func_defaults          # => (42,)
m.run()                              # print {'ans':42}

我尝试使用 types.CodeType(我不太明白)作为函数(不是方法)并让它工作(好吧不会失败),但是添加的关键参数没有显示在函数的 kargs 字典中(它只打印 {})

只需对当前实例进行更改。实际上,我现在正在使用一个类(我认为我是 OO)所以我想用一个类方法来做,但是一个函数可能更好。比如:

def wrapped_run(**kargs):
    def run(**key_args):
        print key_args

    return wrap-the-run-function(run, kargs) 

run = wrapped_run(ans=42)

run.func_code.co_argcount  # => 1
run.func_code.co_varnames  # => ('ans','key_args')  ## keep the 'key_args' or not
run.func_defaults          # => (42,)
run()                      # print {'ans':42}

欢迎任何建议或想法。

关于上下文的一点:

Module 类是某种函数包装器,可用于将低端函数自动包含在数据流系统中,但添加中间过程。我希望模块运行函数(实际上,它可能是 __call___ 函数)具有正确的 API,以便数据流系统能够很好地透明地生成正确的模块输入。

我正在使用 python 2.7

【问题讨论】:

  • 值得一问,您是想将函数添加到绑定方法还是未绑定方法(例如,它们应该只影响当前实例还是类的每个实例?)
  • Sooo,您想要一个可以在运行时配置的函数“管道”,以便数据通过?
  • @mgilson,对,它仅适用于当前实例
  • @SpencerRathbun,我为数据流系统 (OpenAlea) 开发模块,我想为我制作的一些特殊类型的函数自动包装

标签: python dynamic methods named-parameters


【解决方案1】:

您可能正在寻找这样的东西:

class Module(object):
    def __init__(self, **kargs):
        old_run = self.run.im_func
        def run(self,**kwargs):
            kargs_local = kargs.copy()
            kargs.update(kwargs)
            return old_run(self,**kargs)
        self.run = run.__get__(self,Module)

    def run(self,**kargs):
        print kargs

m1 = Module(foo=3,bar='baz')
m1.run()
print type(m1.run)

m2 = Module(foo=4,qux='bazooka')
m2.run()
print type(m2.run)

我刚刚围绕前一个函数创建了一个包装实例方法。 (部分灵感来自this post)。

或者:

from functools import partial
from types import MethodType

class Module(object):
    def __init__(self, **kargs):
        self.run = MethodType(partial(self.run.im_func,**kargs),self,Module)

    def run(self,**kargs):
        print kargs

但这仍然没有提供您正在寻找的 API...

【讨论】:

  • 这个答案很好,因为我是一种方法,但 API 仍然不正确。包装方案很有趣。
  • @Juh_ -- “API 不太正确”到底是什么意思。你想要它是什么?
  • 方法的函数对象的co_argcount、co_varnames等。使用 ipython 进行的一个简单测试是执行“m.run?”并显示预期的 API: m.run(self, foo=3, bar='baz', **kargs)
  • @Juh_ -- 我知道的唯一方法是构建一个字符串 exec 它来创建新函数。 (这实际上是 decorator 模块所做的)。
  • @Juh_ -- 我已经阅读了源代码。 (注意我说的是“装饰器 module”,它是第 3 方扩展)。还应该指出的是,您想要做的事情有些模棱两可。字典是无序的,而函数参数是有序的。无论您做什么,都会出现不匹配的情况。
【解决方案2】:

kwargs 是一个字典,我们需要做的就是保存它以备后用。然后用户可以用他们的值覆盖它。

class Module(object):
    def __init__(self, **kwargs):
        self.defaults = kwargs
    def run(**kwargs):
        values = dict(self.defaults.items() + kwargs.items())
        print values

编辑

您是否正在寻找lambda 函数生成?

def wrapfunc(**kwargs):
    def run(kwargs):
        print kwargs
    return lambda x: run(dict(kwargs.items() + x.items()))

run = wrapfunc(ans=42)
run({})
run({'ans':12})

【讨论】:

  • @mgilson 因为默认值会覆盖特定传入的选项。
  • 我的意思不是在run方法中获取默认参数,而是让run方法API适合函数包装工具
  • @SpencerRathbun -- 啊,对。没想到。 (好答案)
  • 这是一个非常简单的解决方案。我将不得不检查 lambda 是否可以包装到我使用的数据流系统中。没有理由不...
  • @Juh_ 目前我的语法有误,但解决方案本身应该成立。
【解决方案3】:

为了结束,我给出找到的唯一解决方案:使用exec (由 mgilson 提出)

import os, new

class DynamicKargs(object):
    """
    Class that makes a run method with same arguments
    as those given to the constructor
    """
    def __init__(self, **kargs):
        karg_repr = ','.join([str(key)+'='+repr(value) \
                              for key,value in kargs.iteritems()])
        exec 'def run(self,' + karg_repr + ',**kargs):\n    return self._run(' + karg_repr + ',**kargs)'

        self.run = new.instancemethod(run, self)

    def _run(self, **kargs):
        print kargs

# this can also be done with a function
def _run(**kargs):
    print kargs

def dynamic_kargs(**kargs):
    karg_repr = ','.join([str(key)+'='+repr(value) for key,value in kargs.iteritems()])
    exec 'def run(' + karg_repr + ',**kargs):\n    return _run(' + karg_repr + ',**kargs)'
    return run


# example of use
# --------------
def example():
    dyn_kargs = DynamicKargs(question='ultimate', answer=42)
    print 'Class example \n-------------'
    print 'var number:', dyn_kargs.run.im_func.func_code.co_argcount
    print 'var names: ', dyn_kargs.run.im_func.func_code.co_varnames
    print 'defaults:  ', dyn_kargs.run.im_func.func_defaults
    print 'run print: ', 
    dyn_kargs.run()
    print ''

    dyn_kargs = dynamic_kargs(question='foo', answer='bar')
    print 'Function example \n----------------'
    print 'var number:', dyn_kargs.func_code.co_argcount
    print 'var names: ', dyn_kargs.func_code.co_varnames
    print 'defaults:  ', dyn_kargs.func_defaults
    print 'run print: ', 
    dyn_kargs()

example 函数打印:

Class example 
-------------
var number: 3
var names:  ('self', 'answer', 'question', 'kargs')
defaults:   (42, 'ultimate')
run print:  {'answer': 42, 'question': 'ultimate'}

Function example 
----------------
var number: 2
var names:  ('answer', 'question', 'kargs')
defaults:   ('bar', 'foo')
run print:  {'answer': 'bar', 'question': 'foo'}

但是:

  • 如果参数值不能很好地由它们的repr 表示,则可能会出现问题
  • 我觉得太复杂了(所以不是pythonic),个人没用过

【讨论】:

    【解决方案4】:

    这听起来不是个好主意。与其纠结函数签名,不如定义一组默认值作为实例变量,并在函数中使用它:

    class Module(object):
        def __init__(self, **kwargs):
            self.run_defaults = kwargs
        def run(self, **kwargs):
            actual_values = self.run_defaults.copy()
            actual_values.update(kwargs)
            print actual_values
    

    【讨论】:

    • 另外,为什么要复制?为什么不直接使用kwargs.update(self.run_defaults)?现在像往常一样使用kwargs
    • 这是错误的方式:这样默认值会覆盖 kwargs,这似乎是不希望的。
    • 我的意思不是在run方法中获取默认参数,而是让run方法API适合函数包装工具
    猜你喜欢
    • 1970-01-01
    • 2015-05-29
    • 2016-06-05
    • 2021-04-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多