I have a need to create many similar functions in class definitions using their properties. To me, it makes perfect sense to use partial functions for this. However, the properties are not passing what I want to the partial methods (e.g. the property object is being passed, not what it resolves to).
Sample code:
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)
This yields:
> <__main__.Foo object at 0x04326A50>
> <property object at 0x0432A4E0>
> bar
What I want is:
> <__main__.Foo object at 0x04326A50>
> foo
> bar
In case there's a difference, I am also using descriptor classes sometimes instead of properties. It has the same error:
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)
This yields:
<__main__.Foo object at 0x01427690>
<__main__.Child object at 0x01427190>
foobar
When I want:
<__main__.Foo object at 0x01427690>
'child argument was b'
foobar
A
partialmethod
object will only handle descriptor delegation for the function object, no for any arguments that are descriptors themselves. At the time thepartialmethod
object is created, there is not enough context (no class has been created yet, let alone an instance of that class), and apartialmethod
object should not normally act on the arguments you added.You could write your own descriptor object that would do the delegation:
This binds any descriptors in the arguments as late as possible, to the same context the method is bound to. This means your properties are looked up when looking up the partial method, not when the
partialmethod
is created nor when the method is called.Applying this to your examples then produces the expected output:
The binding of any descriptor arguments takes place at the same time the method is bound; take this into account when storing methods for delayed calling:
If this is an issue, don't use
partialmethod
objects; use a decorator(like) approach instead:and use this by passing in the defaults, then the function:
but it can be used as a decorator too:
This resolves the descriptor defaults at calling time: