Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]
Groups > comp.lang.python > #80218 > unrolled thread
| Started by | Roberto Martínez <robertomartinezp@gmail.com> |
|---|---|
| First post | 2014-11-04 20:01 +0100 |
| Last post | 2014-11-07 15:18 +1100 |
| Articles | 2 — 2 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.
Re: [Python-Dev] Dinamically set __call__ method Roberto Martínez <robertomartinezp@gmail.com> - 2014-11-04 20:01 +0100
Re: [Python-Dev] Dinamically set __call__ method Steven D'Aprano <steve+comp.lang.python@pearwood.info> - 2014-11-07 15:18 +1100
| From | Roberto Martínez <robertomartinezp@gmail.com> |
|---|---|
| Date | 2014-11-04 20:01 +0100 |
| Subject | Re: [Python-Dev] Dinamically set __call__ method |
| Message-ID | <mailman.15463.1415127726.18130.python-list@python.org> |
[Multipart message — attachments visible in raw view] — view raw
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.
Thank you all.
Best regards,
Roberto
(Ethan, sorry for posting to python-dev, I thought that it was an
implementation detail of CPython 3.X)
On Tue, Nov 4, 2014 at 7:23 PM, Ethan Furman <ethan@stoneleaf.us> wrote:
> This list is for the development _of_ Python, not development _with_
> Python.
>
> Try asking on Python List.
>
> (forwarding...)
>
>
> On 11/04/2014 08:52 AM, Roberto Martínez wrote:
>
>>
>> I am trying to replace dinamically the __call__ method of an object using
>> setattr.
>> Example:
>>
>> $ cat testcall.py
>> class A:
>> def __init__(self):
>> setattr(self, '__call__', self.newcall)
>>
>> def __call__(self):
>> print("OLD")
>>
>> def newcall(self):
>> print("NEW")
>>
>> a=A()
>> a()
>>
>> I expect to get "NEW" instead of "OLD", but in Python 3.4 I get "OLD".
>>
>> $ python2.7 testcall.py
>> NEW
>> $ python3.4 testcall.py
>> OLD
>>
>> I have a few questions:
>>
>> - Is this an expected behavior?
>> - Is possible to replace __call__ dinamically in Python 3? How?
>>
>
> In 2.7 that would be a classic class, about which I know little.
>
> In 3.x you have a new class, one which inherits from 'object'. When you
> replace __call__ you need to replace it the class, not on the instance:
>
> setattr(__self__.__class__, self.newcall)
>
> --
> ~Ethan~
>
[toc] | [next] | [standalone]
| From | Steven D'Aprano <steve+comp.lang.python@pearwood.info> |
|---|---|
| Date | 2014-11-07 15:18 +1100 |
| Message-ID | <545c47fa$0$13005$c3e8da3$5496439d@news.astraweb.com> |
| In reply to | #80218 |
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.
This is somewhat of a code smell. It's a bit whiffy, which doesn't
*necessarily* mean it is off, only that it could be. You should think hard
about your use-case and consider alternative strategies.
Like, the strategy design pattern :-)
The real problem here is that the individual functions have different
signatures. If they had the same signature, then you could bake that into
the __call__ method and simply call a per-instance method:
class X:
# Variation on the strategy design pattern.
def __call__(self, spam, eggs=12):
method = getattr(self, 'my_custom_method', None)
if method is None:
# Default behaviour.
...
else:
return method(self, spam, eggs)
Then simply add a `my_custom_method` function to the individual instances.
The different functions would provide different algorithms
(implementations) for the same task, with the same interface. This is
exactly the problem with the Strategy design pattern is supposed to solve.
But the smelly part here is that your custom functions take different
signatures, which suggests that they aren't providing different algorithms
for the same task. Different signatures means that they are *not*
interchangeable, that they have different calling conventions and
presumably do different things:
# This only works in Python 2 with "classic (old-style) classes"
a, b, c = [X() for i in range(3)]
a.__call__ = lambda self: self.spam + 1
b.__call__ = lambda self, x, y: (self.eggs or x)*y
c.__call__ = lambda self, value: setattr(self, "spam", value)
That's a strong violation of the expectation that any two instances of the
same class should be more or less interchangeable, with at most a few
exceptional cases (e.g. you can't swap the int instance 0 for the instance
2 in the expression x/2). But it's not *always* wrong, functions themselves
are all instances of FunctionType, and every instance does something
different when called. E.g. while you could swap math.sin for math.cos,
since both take the same parameters, you cannot swap math.sin for len.
Nevertheless, I urge you strongly to think hard about what problem you are
really trying to solve. "Dynamically override __call__" is just a means to
an end. Perhaps there is something less magically you can do to solve the
problem? Is there some way to ensure that all the instances have the same
interface? This might be easy, given that they can store per-instance
state. If instance "a" needs an extra parameter that other instances don't,
perhaps it can take it from an instance attribute instead of an argument.
--
Steven
[toc] | [prev] | [standalone]
Back to top | Article view | comp.lang.python
csiph-web