[ZODB-Dev] Persistent-derived class instances always callable

Tim Peters tim at zope.com
Fri Oct 17 23:04:59 EDT 2003


[Shane Hathaway]
> Checking tp_call is insufficient.  Objects should have the
> opportunity to tell callable() whether or not they are callable.
> Depending on tp_call means that the callability decision is made by
> the type of the object rather than the object itself.

But that's Python -- Python has class-based inheritance, and an object's
behavior in Python is intended to be determined by the object's class/type.
Same thing with whether an object can convert itself to a string, get added
to 5, or reveal a pickleable state -- the class/type gets asked about all
this stuff, not the instance.

The trend has been to make that stronger rather than weaker:

>>> def normal_str(self):
	return "normal str"

>>> def deviant_str():
	return "deviant str"

>>> class C: pass

>>> C.__str__ = normal_str
>>> c = C()
>>> str(c)
'normal str'
>>> c.__str__ = deviant_str
>>> str(c)
'deviant str'

The point there is that instances of old-style classes look up method names
in the instance dict first, so that an instance of C can have its own custom
version of, e.g., __str__.  But this wasn't deliberately intended behavior,
it simply fell out of the way classes and instances got implemented at
first.

Guido didn't think twice about "breaking that" for new-style classes, and
they in fact don't work that way; here's exactly the same stuff except for
making C a new-style class (by inheriting from object):

>>> class C(object): pass

>>> C.__str__ = normal_str
>>> c = C()
>>> str(c)
'normal str'
>>> c.__str__ = deviant_str
>>> str(c)  #  still looks in the class dict for __str__
'normal str'
>>>


> Checking for the __call__ attribute may not be the best approach
> (since it has demonstrated flaws), but we need something better than
> relying on tp_call.

I'm unclear why it's needed -- in over a decade of programming Python, the
first time I used callable() was when typing the last reply <wink>.

If it is needed, and you don't want to fight a losing battle with the
language design, then it's going to be a user-land convention.  For example,
create an ICallable interface, and use the interface machinery to declare
with objects think they're callable, then check an object to see whether it
implements ICallable.

Or maybe ExtensionClass.Base should grow a subclass
ExtensionCLass.BaseCallable, the former not setting tp_call, the latter
setting it.  If the C layouts are otherwise the same, Python should let you
change change an object's __class__ on the fly, and then the fact that a
non-NULL tp_call provides a callable function won't fight with the different
semantics you want.

As is, ExtensionClass instances truly are callable as far as Python is
concerned (they have a tp_call slot, which Python calls), and trying to
convince Guido that "well, ya, they're callable, but not in a sense that's
always useful to this particular app, so Python should grow some other idea
of 'callable' that does make sense to this app" is going to be a hard sell.




More information about the ZODB-Dev mailing list