Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > comp.lang.python > #80222 > unrolled thread

Re: [Python-Dev] Dinamically set __call__ method

Started byEthan Furman <ethan@stoneleaf.us>
First post2014-11-04 11:12 -0800
Last post2014-11-12 11:58 -0700
Articles 16 — 7 participants

Back to article view | Back to comp.lang.python

This discussion starts older than the indexed window; earlier articles aren't shown. The article labeled Started by below is the oldest one visible, not the original post.


Contents

  Re: [Python-Dev] Dinamically set __call__ method Ethan Furman <ethan@stoneleaf.us> - 2014-11-04 11:12 -0800
    Re: [Python-Dev] Dinamically set __call__ method John Ladasky <john_ladasky@sbcglobal.net> - 2014-11-06 12:21 -0800
      Re: [Python-Dev] Dinamically set __call__ method dieter <dieter@handshake.de> - 2014-11-07 07:59 +0100
      Re: [Python-Dev] Dinamically set __call__ method Ethan Furman <ethan@stoneleaf.us> - 2014-11-07 04:58 -0800
      Re: [Python-Dev] Dinamically set __call__ method dieter <dieter@handshake.de> - 2014-11-08 07:50 +0100
      Re: [Python-Dev] Dinamically set __call__ method Ethan Furman <ethan@stoneleaf.us> - 2014-11-08 11:49 -0800
        Re: [Python-Dev] Dinamically set __call__ method Gregory Ewing <greg.ewing@canterbury.ac.nz> - 2014-11-09 11:31 +1300
          Re: [Python-Dev] Dinamically set __call__ method Ethan Furman <ethan@stoneleaf.us> - 2014-11-08 19:27 -0800
            Re: [Python-Dev] Dinamically set __call__ method Gregory Ewing <greg.ewing@canterbury.ac.nz> - 2014-11-10 00:38 +1300
              Re: [Python-Dev] Dinamically set __call__ method Steven D'Aprano <steve+comp.lang.python@pearwood.info> - 2014-11-10 00:14 +1100
              Re: [Python-Dev] Dinamically set __call__ method Ethan Furman <ethan@stoneleaf.us> - 2014-11-09 12:16 -0800
          Re: [Python-Dev] Dinamically set __call__ method Ian Kelly <ian.g.kelly@gmail.com> - 2014-11-11 00:43 -0700
          Re: [Python-Dev] Dinamically set __call__ method Fabio Zadrozny <fabiofz@gmail.com> - 2014-11-12 13:33 -0200
            Re: [Python-Dev] Dinamically set __call__ method Gregory Ewing <greg.ewing@canterbury.ac.nz> - 2014-11-13 17:20 +1300
              Re: [Python-Dev] Dinamically set __call__ method Fabio Zadrozny <fabiofz@gmail.com> - 2014-11-13 11:38 -0200
          Re: [Python-Dev] Dinamically set __call__ method Ian Kelly <ian.g.kelly@gmail.com> - 2014-11-12 11:58 -0700

#80222 — Re: [Python-Dev] Dinamically set __call__ method

FromEthan Furman <ethan@stoneleaf.us>
Date2014-11-04 11:12 -0800
SubjectRe: [Python-Dev] Dinamically set __call__ method
Message-ID<mailman.15467.1415128339.18130.python-list@python.org>
On 11/04/2014 11:01 AM, Roberto Martínez wrote:
> Yikes, I didn't realize the difference in inheritance.
>
> The thing with this is tricky. I need the change in the instance, not in the class, because I have multiple instances
> and all of them must have different implementations of __call__.
>
> The workaround of calling a different method inside __call__ is not valid for my case because I want to change the
> *signature* of the function also -for introspection reasons.

If you really absolutely positively have to have the signature be correct for each instance, you may to either look at a 
function creating factory, a class creating factory, or a meta-class.  The first option may work if you don't need an 
actual class, both of the last two options will be creating different classes, probably with one instance each (unless 
you have several instances which could share the same __call__).

--
~Ethan~

[toc] | [next] | [standalone]


#80342

FromJohn Ladasky <john_ladasky@sbcglobal.net>
Date2014-11-06 12:21 -0800
Message-ID<c25aa612-bff2-4982-8fb9-5b17a866bead@googlegroups.com>
In reply to#80222
On Tuesday, November 4, 2014 11:12:31 AM UTC-8, Ethan Furman wrote:

> If you really absolutely positively have to have the signature be correct for each instance, you may to either look at a 
> function creating factory, a class creating factory, or a meta-class.

+1.  Overriding __call__() within the class definition, over and over again, with different function, looks awkward to me.

[toc] | [prev] | [next] | [standalone]


#80352

Fromdieter <dieter@handshake.de>
Date2014-11-07 07:59 +0100
Message-ID<mailman.15555.1415343612.18130.python-list@python.org>
In reply to#80342
John Ladasky <john_ladasky@sbcglobal.net> writes:

> On Tuesday, November 4, 2014 11:12:31 AM UTC-8, Ethan Furman wrote:
>
>> If you really absolutely positively have to have the signature be correct for each instance, you may to either look at a 
>> function creating factory, a class creating factory, or a meta-class.
>
> +1.  Overriding __call__() within the class definition, over and over again, with different function, looks awkward to me.

A possibility to get the original approach implemented looks like:

  make "__call__" a descriptor on the class which looks up the real
  method on the instance.

[toc] | [prev] | [next] | [standalone]


#80361

FromEthan Furman <ethan@stoneleaf.us>
Date2014-11-07 04:58 -0800
Message-ID<mailman.15560.1415365135.18130.python-list@python.org>
In reply to#80342
On 11/06/2014 10:59 PM, dieter wrote:
> John Ladasky writes:
>> On Tuesday, November 4, 2014 11:12:31 AM UTC-8, Ethan Furman wrote:
>>
>>> If you really absolutely positively have to have the signature be correct for each instance, you may to either look at a
>>> function creating factory, a class creating factory, or a meta-class.
>>
>> +1.  Overriding __call__() within the class definition, over and over again, with different function, looks awkward to me.
>
> A possibility to get the original approach implemented looks like:
>
>    make "__call__" a descriptor on the class which looks up the real
>    method on the instance.

This still wouldn't get the signatrue correct, though.

--
~Ethan~

[toc] | [prev] | [next] | [standalone]


#80395

Fromdieter <dieter@handshake.de>
Date2014-11-08 07:50 +0100
Message-ID<mailman.15580.1415429455.18130.python-list@python.org>
In reply to#80342
Ethan Furman <ethan@stoneleaf.us> writes:

> On 11/06/2014 10:59 PM, dieter wrote:
>> John Ladasky writes:
>>> On Tuesday, November 4, 2014 11:12:31 AM UTC-8, Ethan Furman wrote:
>>>
>>>> If you really absolutely positively have to have the signature be correct for each instance, you may to either look at a
>>>> function creating factory, a class creating factory, or a meta-class.
>>>
>>> +1.  Overriding __call__() within the class definition, over and over again, with different function, looks awkward to me.
>>
>> A possibility to get the original approach implemented looks like:
>>
>>    make "__call__" a descriptor on the class which looks up the real
>>    method on the instance.
>
> This still wouldn't get the signatrue correct, though.

Why not? Once the descriptor is resolved, you get the final
instance method - with the correct signature.

[toc] | [prev] | [next] | [standalone]


#80402

FromEthan Furman <ethan@stoneleaf.us>
Date2014-11-08 11:49 -0800
Message-ID<mailman.15584.1415476169.18130.python-list@python.org>
In reply to#80342
On 11/07/2014 10:50 PM, dieter wrote:
> Ethan Furman <ethan@stoneleaf.us> writes:
>
>> On 11/06/2014 10:59 PM, dieter wrote:
>>> John Ladasky writes:
>>>> On Tuesday, November 4, 2014 11:12:31 AM UTC-8, Ethan Furman wrote:
>>>>
>>>>> If you really absolutely positively have to have the signature be correct for each instance, you may to either look at a
>>>>> function creating factory, a class creating factory, or a meta-class.
>>>>
>>>> +1.  Overriding __call__() within the class definition, over and over again, with different function, looks awkward to me.
>>>
>>> A possibility to get the original approach implemented looks like:
>>>
>>>     make "__call__" a descriptor on the class which looks up the real
>>>     method on the instance.
>>
>> This still wouldn't get the signatrue correct, though.
>
> Why not? Once the descriptor is resolved, you get the final
> instance method - with the correct signature.

Hmmm... well, it wouldn't be correct on a class lookup, but, yeah, it would be correct for an instance lookup -- and 
maybe that's good enough for the OP.

--
~Ethan~

[toc] | [prev] | [next] | [standalone]


#80407

FromGregory Ewing <greg.ewing@canterbury.ac.nz>
Date2014-11-09 11:31 +1300
Message-ID<cc7jtiFubltU1@mid.individual.net>
In reply to#80402
Ethan Furman wrote:
>>
>>> On 11/06/2014 10:59 PM, dieter wrote:
>>>
>>>> A possibility to get the original approach implemented looks like:
>>>>
>>>>     make "__call__" a descriptor on the class which looks up the real
>>>>     method on the instance.
>>>
>>> This still wouldn't get the signatrue correct, though.
>>
>> Why not? Once the descriptor is resolved, you get the final
>> instance method - with the correct signature.
> 
> Hmmm... well, it wouldn't be correct on a class lookup, but, yeah, it 
> would be correct for an instance lookup -- and maybe that's good enough 
> for the OP.

Seems to depend on how you get hold of the object you're
inspecting the signature of. I did an experiment:

   class C(object):

   @property
     def __call__(self):
       return self.call

   def f(x, y):
     print("Called f with %s, %s" % (x, y))

   c = C()
   c.call = f
   c(17, 42)

prints:
   Called f with 17, 42

This works too:

   import inspect
   print(inspect.getargspec(c.__call__))

prints:
   ArgSpec(args=['x', 'y'], varargs=None, keywords=None, defaults=None)

But

   print(inspect.getargspec(c))

doesn't work:

   TypeError: <__main__.C object at 0x479250> is not a Python function

(BTW, I'm actually surprised that this technique makes c callable.
There must be more going on that just "look up __call__ in the class
object", because evaluating C.__call__ just returns the descriptor
and doesn't invoking the descriptor mechanism.)

-- 
Greg

[toc] | [prev] | [next] | [standalone]


#80410

FromEthan Furman <ethan@stoneleaf.us>
Date2014-11-08 19:27 -0800
Message-ID<mailman.15590.1415503665.18130.python-list@python.org>
In reply to#80407
On 11/08/2014 02:31 PM, Gregory Ewing wrote:
>
> Seems to depend on how you get hold of the object you're
> inspecting the signature of. I did an experiment:
>
>    class C(object):
>
>    @property
>      def __call__(self):
>        return self.call
>
>    def f(x, y):
>      print("Called f with %s, %s" % (x, y))
>
>    c = C()
>    c.call = f
>    c(17, 42)
>
> prints:
>    Called f with 17, 42
>
> This works too:
>
>    import inspect
>    print(inspect.getargspec(c.__call__))
>
> prints:
>    ArgSpec(args=['x', 'y'], varargs=None, keywords=None, defaults=None)
>
> But
>
>    print(inspect.getargspec(c))
>
> doesn't work:
>
>    TypeError: <__main__.C object at 0x479250> is not a Python function
>
> (BTW, I'm actually surprised that this technique makes c callable.
> There must be more going on that just "look up __call__ in the class
> object", because evaluating C.__call__ just returns the descriptor
> and doesn't invoking the descriptor mechanism.)

Looks like you found a bug in inspect.getargspec.

And the thing going on is the normal python behavior (in __getattribute__, I believe) of examining the returned 
attribute to see if it is a descriptor, and if so invoking it.

--
~Ethan~

[toc] | [prev] | [next] | [standalone]


#80424

FromGregory Ewing <greg.ewing@canterbury.ac.nz>
Date2014-11-10 00:38 +1300
Message-ID<cc9210Fans4U1@mid.individual.net>
In reply to#80410
Ethan Furman wrote:
> And the thing going on is the normal python behavior (in 
> __getattribute__, I believe) of examining the returned attribute to see 
> if it is a descriptor, and if so invoking it.

Only if you look it up through the instance, though.
Normally, if you look up an attribute on a class,
the descriptor protocol doesn't get triggered.

-- 
Greg

[toc] | [prev] | [next] | [standalone]


#80429

FromSteven D'Aprano <steve+comp.lang.python@pearwood.info>
Date2014-11-10 00:14 +1100
Message-ID<545f68a0$0$12993$c3e8da3$5496439d@news.astraweb.com>
In reply to#80424
Gregory Ewing wrote:

> Ethan Furman wrote:
>> And the thing going on is the normal python behavior (in
>> __getattribute__, I believe) of examining the returned attribute to see
>> if it is a descriptor, and if so invoking it.
> 
> Only if you look it up through the instance, though.
> Normally, if you look up an attribute on a class,
> the descriptor protocol doesn't get triggered.

Since this whole thread is about giving instances their own individual
__call__ methods, I don't think that doing the look-up on the class is part
of the requirements :-)

This seems to work for me:



class call_instance(object):
    def __get__(self, obj, cls=None):
        if cls is None:  cls = type(obj)
        if obj is None:  obj = cls
        return obj.my_call

class SomeClass(object):
    __call__ = call_instance()


a = SomeClass()
b = SomeClass()
c = SomeClass()
c.eggs = 23

from types import MethodType
a.my_call = lambda x, y=1: x/y
b.my_call = lambda spam: str(spam).upper()
c.my_call = MethodType(lambda self: self.eggs + 1, c)




-- 
Steven

[toc] | [prev] | [next] | [standalone]


#80438

FromEthan Furman <ethan@stoneleaf.us>
Date2014-11-09 12:16 -0800
Message-ID<mailman.15605.1415564188.18130.python-list@python.org>
In reply to#80424
On 11/09/2014 03:38 AM, Gregory Ewing wrote:
> Ethan Furman wrote:
>>
>> And the thing going on is the normal python behavior (in __getattribute__, I believe) of examining the returned
>> attribute to see if it is a descriptor, and if so invoking it.
>
> Only if you look it up through the instance, though.
> Normally, if you look up an attribute on a class,
> the descriptor protocol doesn't get triggered.

Hrmm, maybe we're not talking about the same thing.  I was responding to your comment:

> (BTW, I'm actually surprised that this technique makes c callable.
> There must be more going on that just "look up __call__ in the class
> object", because evaluating C.__call__ just returns the descriptor
> and doesn't invoking the descriptor mechanism.)

which seems to clash with what you just said.

--
~Ethan~

[toc] | [prev] | [next] | [standalone]


#80505

FromIan Kelly <ian.g.kelly@gmail.com>
Date2014-11-11 00:43 -0700
Message-ID<mailman.15648.1415691870.18130.python-list@python.org>
In reply to#80407
On Sat, Nov 8, 2014 at 3:31 PM, Gregory Ewing
<greg.ewing@canterbury.ac.nz> wrote:
> (BTW, I'm actually surprised that this technique makes c callable.
> There must be more going on that just "look up __call__ in the class
> object", because evaluating C.__call__ just returns the descriptor
> and doesn't invoking the descriptor mechanism.)

But of course it doesn't just lookup C.__call__, because it has to
bind the method to the instance before calling it, which means
invoking the descriptor protocol. The actual lookup is more like:

type(a).__dict__['__call__'].__get__(a, type(a))

[toc] | [prev] | [next] | [standalone]


#80610

FromFabio Zadrozny <fabiofz@gmail.com>
Date2014-11-12 13:33 -0200
Message-ID<mailman.15726.1415806493.18130.python-list@python.org>
In reply to#80407

[Multipart message — attachments visible in raw view] — view raw

On Tue, Nov 11, 2014 at 5:43 AM, Ian Kelly <ian.g.kelly@gmail.com> wrote:

> On Sat, Nov 8, 2014 at 3:31 PM, Gregory Ewing
> <greg.ewing@canterbury.ac.nz> wrote:
> > (BTW, I'm actually surprised that this technique makes c callable.
> > There must be more going on that just "look up __call__ in the class
> > object", because evaluating C.__call__ just returns the descriptor
> > and doesn't invoking the descriptor mechanism.)
>
> But of course it doesn't just lookup C.__call__, because it has to
> bind the method to the instance before calling it, which means
> invoking the descriptor protocol. The actual lookup is more like:
>
> type(a).__dict__['__call__'].__get__(a, type(a))
> --
> https://mail.python.org/mailman/listinfo/python-list
>


As a reference, I recently found a blog post related to that:
http://lucumr.pocoo.org/2014/8/16/the-python-i-would-like-to-see/ (the
Slots part comments on that).

It does seem a bit counter-intuitive that this happens the way it does
though, so, can someone from python-dev give some background of why that's
the way it is? i.e.: instead of the approach which would seem simpler which
would do getattr(a, '__call__') instead of
type(a).__dict__['__call__'].__get__(a, type(a)) -- it seems to me it's
mostly because of historical reasons, but I'm really curious why is it so
(and if maybe it's something which python-dev would consider worth changing
in the future -- not sure how much could break because of that though).

Thanks and Best Regards,

Fabio

[toc] | [prev] | [next] | [standalone]


#80667

FromGregory Ewing <greg.ewing@canterbury.ac.nz>
Date2014-11-13 17:20 +1300
Message-ID<ccips8Fs5j8U1@mid.individual.net>
In reply to#80610
Fabio Zadrozny wrote:
> can someone from python-dev give some background of why 
> that's the way it is?

It's because, with new-style classes, a class is also an
instance (of class "type" or a subclass thereof). So
without that rule, it would be ambiguous whether a dunder
method applied to instances of a class or to the class
itself.

 > and if maybe it's something which python-dev would consider worth
> changing in the future -- not sure how much could break because of that 
> though

Something fairly fundamental that would break is classs
instantiation! You instantiate a class by calling it, so if
a(x) were implemented as a.__call__(x), and class C had
a __call__ method, then C() would invoke that method
instead of instantiating C.

-- 
Greg

[toc] | [prev] | [next] | [standalone]


#80684

FromFabio Zadrozny <fabiofz@gmail.com>
Date2014-11-13 11:38 -0200
Message-ID<mailman.15774.1415885965.18130.python-list@python.org>
In reply to#80667

[Multipart message — attachments visible in raw view] — view raw

On Thu, Nov 13, 2014 at 2:20 AM, Gregory Ewing <greg.ewing@canterbury.ac.nz>
wrote:

> Fabio Zadrozny wrote:
>
>> can someone from python-dev give some background of why that's the way it
>> is?
>>
>
> It's because, with new-style classes, a class is also an
> instance (of class "type" or a subclass thereof). So
> without that rule, it would be ambiguous whether a dunder
> method applied to instances of a class or to the class
> itself.
>
> > and if maybe it's something which python-dev would consider worth
>
>> changing in the future -- not sure how much could break because of that
>> though
>>
>
> Something fairly fundamental that would break is classs
> instantiation! You instantiate a class by calling it, so if
> a(x) were implemented as a.__call__(x), and class C had
> a __call__ method, then C() would invoke that method
> instead of instantiating C.
>
>
Hi Gregory,

Thanks for the explanation -- it still does seem a bit surprising from an
end-user point of view, but it does make more sense now :)

Best Regards,

Fabio

[toc] | [prev] | [next] | [standalone]


#80620

FromIan Kelly <ian.g.kelly@gmail.com>
Date2014-11-12 11:58 -0700
Message-ID<mailman.15735.1415818783.18130.python-list@python.org>
In reply to#80407
On Wed, Nov 12, 2014 at 8:33 AM, Fabio Zadrozny <fabiofz@gmail.com> wrote:
> As a reference, I recently found a blog post related to that:
> http://lucumr.pocoo.org/2014/8/16/the-python-i-would-like-to-see/ (the Slots
> part comments on that).
>
> It does seem a bit counter-intuitive that this happens the way it does
> though, so, can someone from python-dev give some background of why that's
> the way it is? i.e.: instead of the approach which would seem simpler which
> would do getattr(a, '__call__') instead of
> type(a).__dict__['__call__'].__get__(a, type(a)) -- it seems to me it's
> mostly because of historical reasons, but I'm really curious why is it so
> (and if maybe it's something which python-dev would consider worth changing
> in the future -- not sure how much could break because of that though).

I'm not "someone from python-dev", but I think it's done to keep the
look-up logic predictable and avoid having to call __getattribute__
and __getattr__ when looking up magic methods. For at least
__getattribute__ and __getattr__ themselves we *can't* invoke those
methods while trying to look them up, so there's a case to be made for
consistency in that this way we don't have to remember which magic
methods use __getattribute__ and which ones don't. Performance is also
part of it, since these methods tend to be invoked frequently.

[toc] | [prev] | [standalone]


Back to top | Article view | comp.lang.python


csiph-web