2015-06-17 2 views
1

Мне нужно создать множество подобных функций в определениях классов, используя их свойства. Для меня имеет смысл использовать для этого частичные функции. Однако свойства не передают то, что я хочу для частичных методов (например, передается объект свойства, а не то, что он разрешает).Как получить доступ к свойству класса с помощью частичного метода?

Пример кода:

from functools import partialmethod 

def mk_foobar(*args): 
    for a in args: 
     print(a) 
    print('bar') 

class Foo(object): 
    @property 
    def foo(self): 
     return 'foo' 
    echo = partialmethod(mk_foobar, foo) 

Это дает:

> <__main__.Foo object at 0x04326A50> 
> <property object at 0x0432A4E0> 
> bar 

Что я хочу:

> <__main__.Foo object at 0x04326A50> 
> foo 
> bar 

В случае, если есть разница, я также использую классы дескрипторов иногда вместо свойств. Она имеет ту же ошибку:

from functools import partialmethod 

class Child(object): 
    def __init__(self, item): 
     self.item = item 
    def __get__(self, instance, objtype): 
     return 'child argument was: %s' % self.item 

def mk_foobar(*args): 
    for a in args: 
     print(a) 
    print('foobar') 

class Foo(object): 
    a = Child('b') 
    echo = partialmethod(mk_foobar, a) 

Это дает:

<__main__.Foo object at 0x01427690> 
<__main__.Child object at 0x01427190> 
foobar 

Когда я хочу:

<__main__.Foo object at 0x01427690> 
'child argument was b' 
foobar 
+0

Это связано с тем, что при выполнении тела класса есть еще один класс *, не говоря уже о экземплярах, для привязки протокола дескриптора. Вы не можете делать то, что хотите, с помощью 'partialmethod()'. –

+0

Что такое «лучшие практики» для этого? Как насчет создания всех методов с частичным внутри __init__? –

+0

Затем вы получите вывод дескриптора только один раз, на '__init__'. Предположительно, вы хотите разрешить дескриптор как можно позже (чтобы вы получили значения свойств «live»). Я написал ответ. –

ответ

1

partialmethod объект будет обрабатывать только дескриптор делегирования для объекта функции, ни для каких-либо аргументов, которые сами дескрипторы. В то время, когда создается объект partialmethod, недостаточно контекста (еще не создан класс, не говоря уже о экземпляре этого класса), а объект partialmethod обычно не должен действовать на добавленные вами аргументы.

Вы можете написать свой собственный объект дескриптора, который будет делать делегации:

from functools import partial, partialmethod 

class partialmethod_with_descriptorargs(partialmethod): 
    def __get__(self, obj, cls): 
     get = getattr(self.func, "__get__", None) 
     result = None 
     if get is not None: 
      new_func = get(obj, cls) 
      if new_func is not self.func: 
       args = [a.__get__(obj, cls) if hasattr(a, '__get__') else a 
         for a in self.args] 
       kw = {k: v.__get__(obj, cls) if hasattr(v, '__get__') else v 
         for k, v in self.keywords.items()} 
       result = partial(new_func, *args, **kw) 
       try: 
        result.__self__ = new_func.__self__ 
       except AttributeError: 
        pass 
     if result is None: 
      # If the underlying descriptor didn't do anything, treat this 
      # like an instance method 
      result = self._make_unbound_method().__get__(obj, cls) 
     return result 

Это связывает любые дескрипторы в рассуждениях как можно позже, в том же контексте метод связан с. Это означает, что ваши свойства выглядят вверх при поиске частичного метода, а не при создании partialmethod и при вызове метода.

Применяя это к вашим примерам затем производит ожидаемый результат:

>>> def mk_foobar(*args): 
...  for a in args: 
...   print(a) 
...  print('bar') 
... 
>>> class Foo(object): 
...  @property 
...  def foo(self): 
...   return 'foo' 
...  echo = partialmethod_with_descriptorargs(mk_foobar, foo) 
... 
>>> Foo().echo() 
<__main__.Foo object at 0x10611c9b0> 
foo 
bar 
>>> class Child(object): 
...  def __init__(self, item): 
...   self.item = item 
...  def __get__(self, instance, objtype): 
...   return 'child argument was: %s' % self.item 
... 
>>> class Bar(object): 
...  a = Child('b') 
...  echo = partialmethod_with_descriptorargs(mk_foobar, a) 
... 
>>> Bar().echo() 
<__main__.Bar object at 0x10611cd30> 
child argument was: b 
bar 

Связывание аргументов дескриптора происходит в то же время этот метод связан; принимать это во внимание при хранении методов отсроченного вызова:

>>> class Baz(object): 
...  _foo = 'spam' 
...  @property 
...  def foo(self): 
...   return self._foo 
...  echo = partialmethod_with_descriptorargs(mk_foobar, foo) 
... 
>>> baz = Baz() 
>>> baz.foo 
'spam' 
>>> baz.echo() 
<__main__.Baz object at 0x10611e048> 
spam 
bar 
>>> baz_echo = baz.echo # just the method 
>>> baz._foo = 'ham' 
>>> baz.foo 
'ham' 
>>> baz_echo() 
<__main__.Baz object at 0x10611e048> 
spam 
bar 
>>> baz.echo() 
<__main__.Baz object at 0x10611e048> 
ham 
bar 

Если это вопрос, не использовать partialmethod объектов; использовать подход декоратора (как) вместо того, чтобы:

from functools import wraps 

def with_descriptor_defaults(*defaultargs, **defaultkeywords): 
    def decorator(fn): 
     @wraps(fn) 
     def wrapper(self, *args, **kwargs): 
      args = [a.__get__(self, type(self)) if hasattr(a, '__get__') else a 
        for a in defaultargs] + list(args) 
      kw = {k: v.__get__(self, type(self)) if hasattr(v, '__get__') else v for k, v in defaultkeywords.items()} 
      kw.update(kwargs) 
      return fn(self, *args, **kw) 
     return wrapper 
    return decorator 

и использовать это путем передачи по умолчанию, то функция:

class Foo(object): 
    @property 
    def foo(self): 
     return 'foo' 
    echo = with_descriptor_defaults(foo)(mk_foobar) 

, но он может быть использован в качестве декоратора тоже:

class Bar(object): 
    @property 
    def foo(self): 
     return 'foo' 

    @with_descriptor_defaults(foo) 
    def echo(*args): 
     for a in args: 
      print(a) 
     print('bar') 

Это решает дескриптор по умолчанию во время вызывающего:

>>> class Baz(object): 
...  _foo = 'spam' 
...  @property 
...  def foo(self): 
...   return self._foo 
...  echo = with_descriptor_defaults(foo)(mk_foobar) 
... 
>>> baz = Baz() 
>>> baz_echo = baz.echo 
>>> baz._foo = 'ham' 
>>> baz_echo() 
<__main__.Baz object at 0x10611e518> 
ham 
bar 
0

Вы должны получить доступ к foo из контекста экземпляра self.foo. Поэтому вы не можете использовать partialmethod здесь. Определение METHODE:

def echo(self, *args): 
    return mk_foobar(self.foo, *args) 

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

  • Нет связанных вопросов^_^