[Python-Dev] type categories
Oren Tirosh
oren-py-d@hishome.net
Tue, 27 Aug 2002 08:18:28 +0300
On Mon, Aug 26, 2002 at 08:39:44PM -0500, jepler@unpythonic.net wrote:
> On Mon, Aug 26, 2002 at 05:56:52PM -0400, Guido van Rossum wrote:
> > > It gets harder if you want to remove a method or marker. The problem is
> > > that there is currently no way to mask inherited attributes. This will
> > > require either a language extension that will allow you to del them or
> > > using some other convention for this purpose.
> >
> > Can't you use this?
> >
> > def B:
> > def foo(self): pass
> >
> > def C:
> > foo = None # Don't implement foo
>
> This comes closer:
>
> def raise_attributeerror(self):
> raise AttributeError
>
> RemoveAttribute = property(raise_attributeerror)
>
> class A:
> def f(self): print "method A.f"
> def g(self): print "method A.g"
>
> class B:
> f = RemoveAttribute
Yes, that's a good solution. But it should be some special builtin
out-of-band value, not a user-defined property.
> writing 'b.f' will raise AttributeError, but unfortunately hasattr(B, 'f')
> will still return True.
This isn't necessarily a problem but hasattr could be taught about this
out-of-band value.
--
Proposed hierarchy for categories, types and interfaces:
+category
+type
+int
+str
etc.
+interface
+Iattribute
+Icallsignature
+Iunion
+Iintersection
etc.
Both types and interfaces define a set. The set membership test is the
'isinstance' function (implemented by a new slot). For types the set
membership is defined by inheritance - the isinstance handler will get the
first argument's type and crawl up the __bases__ DAG to see if it finds the
itself. Interfaces check the object's form instead of its ancestry.
An Iattribute interface checks for the presence of a single attribute and
applies another interface check to its value. An Icallsignature interface
checks if the argument is a callable object with a specified number of
arguments, default arguments, etc. An Iintersection interface checks that
the argument matches a set of categories.
example:
interface readable:
def read(bytes: int): str
def readline(): str
def readlines(): [str]
is just a more convenient way to write:
readable = Iintersection(
Iattribute('read', Icallsignature(str, ('bytes', int) )),
Iattribute('readline', Icallsignature(str)),
Iattribute('readlines', Icallsignature(Ilistof(str)))
)
The name 'readable' is simply bound to the resulting object; interfaces are
defined by their value, not their name. The types of arguments and return
values will not be checked at first and only serve as documentation. Note
that they don't necessarily have to be types - they can be interfaces, too.
For example, 'str|int' in an interface declaration will be coverted to
Iunion(str, int).
>>>isinstance(file('/dev/null'), readable)
True
>>>isinstance(MyFileLikeClass(), readable)
True
The MyFileLIkeClass or file classes do not have to be explicitly declared
as implementing the readable interface. The benefit of explit inteface
declarations is that you will get an error if you write a method that does
not match the declaration. If you try to implement two conflicting
interfaces this can also be detected immediately - the intersection of the
two interfaces will reduce to the empty interface. For now this will only
catch the same method name with different number of arguments but in the
future it may detect conflicting argument or return value types.
doesn't-have-anything-better-to-do-at-6-am-ly yours,
Oren