[Python-3000] iostack and Oh Oh

tomer filiba tomerfiliba at gmail.com
Fri Dec 1 23:02:00 CET 2006

> Luckily, however, proxies can lie about their __class__ (since Python 2.3)
> and thereby fool isinstance().

yes, well, you try to override __class__ when the real class is actually
in a different process :)

types compare by ID, so when a proxy is created for an object in another
process, you can only set __class__ to a *proxy* to the remote type...
so isinstance fails. i solved the problem with builtin types, but for all other
types, it's impossible.

> I know (I should, since I wrote the code :-) but I think the solution
> could be much cleaner.

having an __isinstance__ would be nice... that way i could do:
def __isinstance__(self, types):
    return isinstance(self.the_real_object, types)

which will ultimately solve all my problems. that, along with generic
functions, really adds power to the language. for example, today's
help() doesn't work well with proxies, so i could "augment" it...
very nice indeed.

> In other words, I just want to use my object with some operations that a
> library provides (or requires).  An "interface" is excise: something I have
> to mess with that doesn't directly relate to my goals for using the library.

that's exactly my point. i hate restating what i'm doing just to make some
tool/compiler/framework happy. i want to express it in code, and let the
tool automatically deduce that -- without me manually adding it as metadata.

that's duplicating work, without adding real information to the object.
it already supports __iter__, so why also derive from BaseIterable?
the best example i can think of is... java: in order for a function to throw
an exception, it must explicitly declare the exception type it may throw:

void Foo() throws Bar {
    throw new Bar();

why can't the compiler deduce that automatically? why should a human
put time into even thinking about that?

> Generic functions are type-safe duck typing because ducklib.quack(ob)
> doesn't rely on ob having a 'quack()' method that means what you think it
> means.  This solves the problem simply and directly, without introducing
> interfaces and ABCs and whatnot.  Either the operation is defined for the
> type or it isn't.

i'm +1 on that, but still, as Guido (and others) said, you need a way to
differentiate a sequence from a mapping, or a list from a string. dispatching
based on *concrete types*, i.e., isinstance(x, dict), is bad, as it means
users *must derive* from dict in order to be dispatched as *dict-like*

- - - - - -

types are something *about* the object, while attributes are something
the object *has*. duck-typing is much more agile/flexible, as it's based on
attributes -- and as it turns out -- it's much more easy to change something
you *have* than something *about* you. (reminds me of The Verve's
Bittersweet Symphony :)

allow me to suggest a solution in the spirit of duck-typing:

class ReadOnlyDict:
    def __getitem__...
    def __iter__...
    __adheres__ = ["dict"] # <<<

class MyList:
    def __getitem__...
    def __iter__...
    __adheres__ = ["list"] # <<<

or once we have class decorators --

class ReadOnlyDict...

this way, ReadOnlyDict simply *states* it adheres to the "dict contract"
(or ability), which may be something completely abstract. the *manuals*
should defines this contract, not code.

a type can just state it adheres to a set of contracts, and the dispatching
mechanism will treat it right.

def use_a_dict(obj : "dict"):
use_a_dict( {1:2, 3:4})       # dispatches as a dict
use_a_dict(ReadOnlyDict(...)) # dispatches as a dict
use_a_dict(MyList(...))       # fails

of course we might want to use type-objects / contract-objects instead of
plain strings, but i wanted to use strings to emphasize the point --
the *contract* has nothing to do with *types* or subclassing.

the contract is something the object *has* instead of something
*about* the object. i can just add a new contract tomorrow,
without worrying about MROs and a name being multiply defined in
several base classes, and whatnot.

also, the concrete type can always be a default contract, i.e., if you don't
explicitly place an "__adheres__" in your class, the class itself is used
instead of the __adheres__ metadata.

(perhaps it should be made a magic method instead of an attribute? donno)


More information about the Python-3000 mailing list