在python中将部分函数转换为方法

paf*_*fcu 11 python

考虑以下(破碎)代码:

import functools                                                            
class Foo(object):                                                          
  def __init__(self):                                                 
    def f(a,self,b):                                            
      print a+b                                           
    self.g = functools.partial(f,1)                             
x=Foo()                                                                     
x.g(2)
Run Code Online (Sandbox Code Playgroud)

我想要做的是获取函数f并部分应用它,从而产生一个函数g(self,b).我想使用此函数作为方法,但这目前不起作用,而是我得到错误

Traceback (most recent call last):
  File "test.py", line 8, in <module>
    x.g(2)
TypeError: f() takes exactly 3 arguments (2 given)
Run Code Online (Sandbox Code Playgroud)

x.g(x,2)然而,这样做是有效的,所以似乎问题是g被认为是"正常"函数而不是类的方法.有没有办法让xg表现得像一个方法(即隐式传递self参数)而不是函数?

Mik*_*ers 22

这里有两个问题.首先,对于要转换为方法的函数,它必须存储在类中,而不是实例上.示范:

class Foo(object):
    def a(*args):
        print 'a', args

def b(*args):
    print 'b', args

Foo.b = b

x = Foo()

def c(*args):
    print 'c', args

x.c = c
Run Code Online (Sandbox Code Playgroud)

因此a,在类定义中定义b的函数是之后分配给类c的函数,并且是分配给实例的函数.看看我们打电话给他们时会发生什么:

>>> x.a('a will have "self"')
a (<__main__.Foo object at 0x100425ed0>, 'a will have "self"')
>>> x.b('as will b')
b (<__main__.Foo object at 0x100425ed0>, 'as will b')
>>> x.c('c will only recieve this string')
c ('c will only recieve this string',)
Run Code Online (Sandbox Code Playgroud)

正如您所看到的,与类一起定义的函数与稍后分配给它的函数之间几乎没有区别.我相信只要没有涉及元类,实际上没有区别,但那是另一次.

第二个问题来自于函数首先如何实际转化为方法; 函数类型实现描述符协议.(有关详细信息,请参阅文档.)简而言之,函数类型有一个特殊的__get__方法,当您对类本身执行属性查找时,会调用该方法.而不是获取函数对象,__get__调用该函数对象的方法,并返回绑定的方法对象(这是提供self参数的对象).

为什么这是个问题?因为functools.partial对象不是描述符!

>>> import functools
>>> def f(*args):
...     print 'f', args
... 
>>> g = functools.partial(f, 1, 2, 3)
>>> g
<functools.partial object at 0x10042f2b8>
>>> g.__get__
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'functools.partial' object has no attribute '__get__'
Run Code Online (Sandbox Code Playgroud)

此时您有很多选择.您可以显式为partial提供self参数:

import functools
class Foo(object):                                                          
    def __init__(self):
        def f(self, a, b):                                            
            print a + b                                           
        self.g = functools.partial(f, self, 1)

x = Foo()
x.g(2)
Run Code Online (Sandbox Code Playgroud)

......或者你会在封闭中嵌入a的自我和价值:

class Foo(object):                                                          
    def __init__(self):                                                 
        a = 1
        def f(b):                                            
            print a + b                                           
        self.g = f

x = Foo()
x.g(2)
Run Code Online (Sandbox Code Playgroud)

这些解决方案当然假设在构造函数中有一个尚未指定的方法将类分配给类,因为您可以很容易地直接在类上定义一个方法来执行您在此处执行的操作.

编辑:这是一个解决方案的想法,假设可以为类而不是实例创建函数:

class Foo(object):
    pass

def make_binding(name):
    def f(self, *args):
        print 'Do %s with %s given %r.' % (name, self, args)
    return f

for name in 'foo', 'bar', 'baz':
    setattr(Foo, name, make_binding(name))

f = Foo()
f.foo(1, 2, 3)
f.bar('some input')
f.baz()
Run Code Online (Sandbox Code Playgroud)

给你:

Do foo with <__main__.Foo object at 0x10053e3d0> given (1, 2, 3).
Do bar with <__main__.Foo object at 0x10053e3d0> given ('some input',).
Do baz with <__main__.Foo object at 0x10053e3d0> given ().
Run Code Online (Sandbox Code Playgroud)


Man*_*dan 9

这会奏效.但我不确定这是不是你想要的

class Foo(object):                                                          
  def __init__(self):                                                 
    def f(a,self,b):                                            
      print a+b                                           
    self.g = functools.partial(f,1, self) # <= passing `self` also.

x = Foo()
x.g(2)
Run Code Online (Sandbox Code Playgroud)

  • -1因为这是一种变通方法,但它不是一个真正的方法 - 它只是存储在实例上的可调用方法.至关重要的是,'Foo.g`不存在. (2认同)

ant*_*ger 6

这只是我认为最正确(也就是pythonic :)解决方法的一个具体例子 - 因为最好的解决方案(类上的定义!)从未被揭示 - @MikeBoers解释其他方面都很可靠.

我已经使用了这种模式(最近用于代理API),并且它在没有任何不规则性的情况下在无数生产时间内存活下来.

from functools import update_wrapper
from functools import partial
from types import MethodType


class Basic(object):

    def add(self, **kwds):
        print sum(kwds.values())

Basic.add_to_one = MethodType(
        update_wrapper(partial(Basic.add, a=1), Basic.add),
        None,
        Basic,
        )


x = Basic()
x.add(a=1, b=9)
x.add_to_one(b=9)
Run Code Online (Sandbox Code Playgroud)

...收益率:

10
10
Run Code Online (Sandbox Code Playgroud)

...钥匙拿回家点这里MethodType(func, inst, cls),这从另一个调用创建一个不受约束的方法(你甚至可以用它来链/绑定实例方法不相关的类实例化......当+叫原来的实例方法将得到BOTH self对象!)

请注意关键字参数的独占使用!虽然可能有更好的方法来处理,但args通常是PITA,因为自我的位置变得不那么可预测.另外,无论如何,IME使用*args,并且**kwds在最底层的功能已证明以后非常有用.