[Python-3000] prototype OO?

tomer filiba tomerfiliba at gmail.com
Fri May 12 10:27:46 CEST 2006


well, i was aiming more to the point of having no types at all: only a
lookup chain for attributes (kinda like the mro), but being mutable.
an object in the lookup chain is any getattr-able object, and the
langauge takes care for it. for example, doing x.y means
* first look at the instance, if it can provide "y"
* else, start climbing up the __mro__ of "x", see if any of those can
provide "y"
* else, raise AttributeError.

that said, types become a meaningless concept. i guess the object
should still retain a __class__ attribute or something like that, but
only for informational purposes (not direct usage)

instead, you'll just rely on duck typing ("the lookup chain") to find the
attributes you want:

def blah(f):
    f.write("blah blah blah")
    f.close()

and, in times you want early error detection, just test for the object
"protocol". this is useful when you do things with side effects, and you
want to prevent silly "oh i passed a wrong object" errors:

def blah(f):
    assert hasattr(f, "write")
    assert hasattr(f, "close")
    f.write("blah blah blah")
    f.close()

but this can get combersome, so i suggested to introduce an "isproto"
function, which is like multiple hasattrs at once, i.e.,

file_protocol = ["write", "read", "close"]
def blah(f):
    assert isproto(f, file_protocol)
    f.write("blah blah blah")
    f.close()

which can be shortened by the new optional typing syntax:
def blah(f : file_protocol):
    f.write("blah blah blah")
    f.close()

and then my goal is fulfilled: types are basically bags of attributes, nothing
magical.

of course we can (as perhaps should) add signatures to those "protocols",
i.e.

file_protocol = {
    "write" : Function(self, str_protocol),
    "read" : Function(self, number_protocol),
    "close" : Function(self),
}

and then isproto can also test the signatures, which is not possible with
today's isinstance.

then types become a flat list of "capabilities", as Talin called it. i guess a
better name can be proposed, but it's only semantics. the key issue is being
flat:

def read_lines_from_file(f : (file_protocol, iter_protocol)):
    for line in f:
        print line
    f.close()

f : (file_protocol, iter_protocol) means f has all the attributes
specified by the
two protocols: it has "write", "read", "close", and "__iter__". it's basically
the union of the two protocols: iter_file_proto = file_proto + iter_proto.

-----

class file:
    """the basic, binary file"""
    def __init__(self, name, mode = "r"):
        self.fd = os.open(name, mode)
    def write(self, data : str_proto):
        os.write(self.fd, data)
    def read(self, count : num_proto):
        return os.read(self.fd, count)
    def close(self):
        os.close(self.fd)

class textfile(file):
    def writeline(self, text : str_proto):
         self.write(text + "\n")
    def readline(self):
         line = ""
         while True:
            ch = self.read(1)
            if ch == "\n": break
            line += ch
         return line
    def __iter__(self):
         while True:
             line = self.readline()
             if not line: break
             yield line

# the lookup chain is [textfile, file]
t = textfile("....")
# calls __init__:
# since textfile doesnt have __init__, it's looked up at file,
# just as it happens today

isproto(t, iter_proto) # True
isproto(t, file_proto) # True
isproto(t, sequence_proto) # False

t.__lookupchain__.append( [1,2,3] ) # note it's an instance not a type!

isproto(t, sequence_proto) # True

# now you can do
t.append("hello")

and it will call the append of the list instance we added to the lookup chain.

------

Raymond Hettinger wrote:
> Boy = Object().clone(gender = 'male')
> def Boy.sayGender(self):
>    print self.gender
> Girl = Goy.clone(gender = 'female')
> m = Girl.clone(name=megan, age=3)
> m.sayGender()

class GenderSayyer:
     def sayGender(self):
         print self.gender

class Person:
     def __init__(self, gender):
         self.gender = gender

boy = Person("male")

# adding or remove "capabilties" at runtime
boy.__lookupchain__.append(GenderSayyer)
boy.sayGender() # male
boy.__lookupchain__.remove(GenderSayyer)
boy.sayGender() # AttributeError

------
Jim Jewett wrote:
> Proxies are certainly the perfect example of something where you would
> want a "just like X, except" object, and would prefer to be able to
> use instances as the prototype.

with this typeless approach:

class LoggingProxy:
    def __init__(self, realobj):
        self.__realobj = realobj
    def __getattr__(self, name):
        log_to_file("proxy now gets %r", % (name,))
        return getattr(self.__realobj, name)

p = LoggingProxy(5)
isproto(p, num_proto) # True
p + 4 # == 9
str(p) # "5"

------

to sum it up, types become meaningless: the "type of x" determines the
"lookup chain of x.y", and protocols are just sets of attributes that
"x should have in order to be considered z", which can also match specific
method signatures, unlike isinstance. this is what i call a typeless
language, and python is half-way there already. it just needs a slight push
over the edge ;-)


-tomer

On 5/11/06, Jim Jewett <jimjjewett at gmail.com> wrote:
> On 5/11/06, tomer filiba <tomerfiliba at gmail.com> wrote:
>
> > class myFrame(derive_of(c.modules.wx.Frame)):
> >     ...
>
> > c.modules.wx.Frame is a proxy (instance) to a remote type, so you can't
> > derive from it directly. therefore, derive_of is
>
> > def derive_of(proxy_type):
> >     class cls(object):
> >         def  __getattr__(self, name):
> >             return getattr(proxy_type, name)
> >     return cls
>
> > which basically means that when the attribute doesn't belong to the
> > instance of local class, it is queried for at the remote class. but it doesnt
> > work, because the method checks for the instance's type.
>
> There are languages (Self; I think even javascript) whose OO is based
> on Prototypes rather than classes.  If an object doesn't have the
> attribute/method, then check its prototype (and so on recursively).
>
> When I have to emulate this in python (or Java), I just try to shove
> all my logic into classmethods, and use at most a singleton instance.
>
> Proxies are certainly the perfect example of something where you would
> want a "just like X, except" object, and would prefer to be able to
> use instances as the prototype.
>
> I don't think prototype OO should replace the current form, but it
> might be worth adding, even in Python 2.X.  Do you have a proposed
> implementation, or even a proposed syntax?
>
> -jJ
>


More information about the Python-3000 mailing list