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
Это связано с тем, что при выполнении тела класса есть еще один класс *, не говоря уже о экземплярах, для привязки протокола дескриптора. Вы не можете делать то, что хотите, с помощью 'partialmethod()'. –
Что такое «лучшие практики» для этого? Как насчет создания всех методов с частичным внутри __init__? –
Затем вы получите вывод дескриптора только один раз, на '__init__'. Предположительно, вы хотите разрешить дескриптор как можно позже (чтобы вы получили значения свойств «live»). Я написал ответ. –