2016-12-09 2 views
1

Рассмотрим следующий код:Использование functool.partialmethod и functool.partial?

import functools 
import inspect 

class Foo: 
    def foo_fn(self, hello, world): 
     print(hello, world) 

class FooWrapper: 
    def __init__(self, foo_class): 
     self._foo = foo_class() 
     for key, value in inspect.getmembers(self._foo): 
      if inspect.ismethod(value): 
       bound_fn = functools.partial(self.foo_wrapper_fn, self, value) 
       setattr(self._foo, key, bound_fn) 

    def foo_wrapper_fn(self_wrapper, self_foo, bound_method, hello, world): 
     bound_method(hello, world) 

def make_foo(Foo): 
    wrapper = FooWrapper(Foo) 
    return wrapper._foo 
a = make_foo(Foo) 
a.foo_fn("hello", "world") 
  1. Как это упорядочение функции foo_wrapper_fn() параметры, такие как (self_wrapper, self_foo, bound_method, hello, world) приходят быть? и не так: self_wrapper, bound_fn, self_foo, hello, world, так как self_wrapper и bound_fn были частично связаны сначала?

  2. Это нормально для возвращения functool.partial(а не functool.partialmethod) называться объектом (a.foo_fn)?

  3. Если я заменяю его functools.partialmethod(self.foo_wrapper_fn, self, value), почему возникает эта ошибка?

    TypeError: 'partialmethod' object is not callable 
    
+0

Нет, он имел в виду, что он передается в Foo в качестве параметра конструктору. –

+0

@ DannyStaple: проверьте, действительно. –

+0

@ Danny Staple: передача класса здесь не требуется. в фактическом производственном коде я использую оболочку com, и я могу только создать экземпляр позже в другом контексте потока, где должны выполняться функции com. – goldcode

ответ

1

Обе первые аргументы тот же объект. self.foo_wrapper_fn связан с экземпляром FooWrapper (как он искал на self), а затем вы указываете частичное пройти в selfснова. Так что действительно, подпись - это всего лишь двойная путаница, и вы можете оставить второй аргумент и просто не пройти секунду, явно self.

Обратите внимание, что вы никогда не проходите мимо self._foo в любом месте.

Порядок просто устанавливается:

  1. Связанный метод, передавая в связанном FooWrapper() экземпляра.
  2. Независимо от позиционных аргументов, которые вы передали в звонок partial(), поэтому self, value. Это пример FooWrapper()снова, а также связанный метод Foo.
  3. любые дополнительные аргументы, которые вы передаете при вызове объекта partial().

Вы есть использовать partial() объект здесь, а не partialmethod(), потому что вы настраиваете атрибуты в экземпляре. A partialmethod() предназначен для использования в качестве объекта дескриптора, например. атрибут класса.

Именно поэтому вы получаете свой TypeError; partialmethod() никогда не связывался. Если он был связан (вызывается __get__ method, передается в экземпляре для привязки), тогда был бы возвращен правильный вызываемый объект.

См Python Descriptor Howto о том, как дескрипторы работы (связанные методы создаются с помощью этого протокола, и partialmethod объектов, а также property, classmethod и staticmethod объекты, все строить на тех же принципах).

Таким образом, можно упростить код:

class FooWrapper: 
    def __init__(self, foo_class): 
     self._foo = foo_class() 
     for key, value in inspect.getmembers(self._foo): 
      if inspect.ismethod(value): 
       bound_fn = functools.partial(self.foo_wrapper_fn, value) 
       setattr(self._foo, key, bound_fn) 

    def foo_wrapper_fn(self, bound_method, hello, world): 
     bound_method(hello, world) 

Если вы должны иметь доступ к исходному Foo(), например, использовать атрибут __self__ на переменную bound_method.

Смежные вопросы