[Python-Dev] proposal for interfaces
Esteban U.C.. Castro
Sat, 28 Sep 2002 08:26:38 -0000
Hi, I have just joined python-dev and I saw your very interesting
for implementing intefaces.
> I have an idea for an interface mechnism for Python, and I'd like to
> anyone likes it before writing an actual PEP. [...]
I like it a lot! Anyway, if it can be implemented in python as is, what
the point of the PEP? Making the 'interface' root class and/or
I have some comments which I thought I would bounce. I'll organize these
attending to the activities they relate to. Don't hesitate to tell me if
sayig something stupid. :)
Define an interface
In your example, it seems that
def default_foo(self, a, b):
"Docstring for foo method."
print "Defaults can be handy."
[I added the a, b arguments to illustrate one point below]
Does the following:=20
- Defines the requirement that all objects that implement Foo have a
- Defines that foo should accept arguments of the form (a, b) maybe?
- Sets a doc string for the foo method.
- Sets a default implementation for the method.
Some questions on this:
- Can an interface only define method names (and maybe argument
I think it would be handy to let it expose attributes.
- Is method names (and maybe format of arguments) the only thing you
'promise' in the interface? In other words, is that the only type of=20
guarantee that code that works against the interface can get? I think
a __check__(self, obj) special method in interfaces would be a simple
way to boost their flexibility.
- For the uses you have given to the prefix_methodname notation so far,
I don't think it's really needed. Isn't the following sufficient?
def foo(self, a, b):
# nothing here; no default definition
def bar(self, a, b):
pass # no docstring, _empty definition_
This has the side effect that a method with no default definition and no
doc string is a SyntaxError. Is this too bad?=20
- It would maybe be hard to figure out what such a method is supposed to
do, so you _should_ provide a docstring.=20
- If you're in a hurry, an empty docstring will do the trick. While in=20
'quick and dirty mode' you probably won't be using interfaces a lot,=20
Defaults look indeed useful, but the really crucial aspect to lay down=20
in an interface definition is what does it guarantee on the objects that
implement it. If amalgamating this with default defs would otherwise=20
obscure it (there's another issue I'm addressing below), I think
belong more properly to a class that implements the interface, not to
interface definition itself.
I guess knowing what other uses you have in mind for the
notation could be useful to decide whether it's warranted.
Check whether an object implements an interface
>From your examples, I get it that an object implements an interface iff
is an instance of a class that implements that interface.=20
So I guess any checking of the requirements expressed by the interface
done at the time when you bind() a class to an interface.
This paradigm perfectly fits static and strongly typed languages, but it
falls a bit short of the flexibility of python IMO. You can do very
things to classes and objects at runtime, which can break any
based on object class.
In your example:
foo_proxy =3D Foo(foo_arg)
x =3D foo_proxy.foo(a, b)
[added a, b again]
imagine foo_proc may only really cares that foo_arg is an object that
a foo() method that takes (a, b) arguments (this is all Foo guarantees).
* Will the Foo() call check this, or will it just check that some class
in foo_arg's bases is bound to the Foo interface?=20
In the second case, if someone has been fiddling with foo_arg or some
of its base classes, foo_arg.foo() may no longer exist or it may have
a different signature.
* Why should Foo() _always_ fail for objects that _do_ meet the
expressed by the interface Foo but have _not_ declared that they
the interface? If the point is to avoid false positives, interfaces=20
with this concern may still make the class check:
def __call__(self, obj):
error =3D __check__(obj)
raise InterfaceError, error
def __check__(self, obj):
if not hasattr(obj, "foo"):
return "No method foo found"
return interface.check_class(self, obj)
Making such check optional allows implicit (not declared) interface=20
satisfaction for those who want it. This should extend the applicability
And this brings up another problem with defaults: they would increase
positives. What if an interface wants to provide defaults for all its
Will then any object match it? This would force additional checking.=20
Even thought this doesn't look like a big issue to me, I think it's
to leave validation for interfaces and implementation for classes.
Declare that an object implements an interface or part of it
* I agree the declaration had better be included in the class
least as an option.=20
* Declarative better than procedural, for this purpose.
* Only classes (not instances) can declare that they implement an
* Indexing notation to 'resolve' methods a bit counterintuitive.
def foo(self, a, b):=20
def clash1(self, x):
def bar(self, x, y):
def clash1(self, a, s, d, f):
# actually equivalent to Foo.clash2
# we should really factor this out in one interface
# but imagine we can't do so for some reason...
# promise that all instances of SomeClass will implement Foo and
__implements__ =3D (Foo, Bar)=20
# automatically assumed to implement Foo.foo() =20
def foo(self, a, b):=20
# There is not automatic name clash resolution. InterfaceError
# we resolve these explicitly
def fclash1(self, x):
fclash1.__implements__ =3D Foo.clash1 # maybe we should require
# to be a
def bclash1(self, x):
bclash1.__implements__ =3D Bar.clash1
clash2.__implements__ =3D (Foo.clash2, Bar.clash2) # or maybe this
# not really needed?
# seems to reflect bad=20
# design anyway
# 'Remove' an interface from a subclass without actually removing it
# the base (or just cut the search with negative result):
__implements__ =3D (-Foo,) # will be found before the 'Foo' in =
# an object that is *not* an instance of a class that implements Foo
# to play the Foo
obj =3D Child()
obj.foo =3D lambda a, b: ...
obj.clash1 =3D lambda x: ...
obj.for_the_fun_of_it =3D lambda: ...
obj.__implements__ =3D (Foo,) # will be found before the '-Foo' in =
obj.for_the_fun_of_it.__implements__ =3D Foo.clash2 # object dict will
This is, to make sure that an object is only accessed in the ways=20
defined in the interface (via the proxy).=20
This should be optional too, but your syntax does this nicely; you
can call Foo() as an assertion of sorts and ignore the result.
Note that the __implements__ method resolution magic would require=20
that you get a proxy, though.
What do you think?