Bindable classes

class sage.misc.bindable_class.BindableClass

Bases: object

Bindable classes

This class implements a binding behavior for nested classes that derive from it. Namely, if a nested class Outer.Inner derives from BindableClass, and if outer is an instance of Outer, then outer.Inner(...) is equivalent to Outer.Inner(outer, ...).


Let us consider the following class Outer with a nested class Inner:

sage: from sage.misc.nested_class import NestedClassMetaclass
sage: class Outer(metaclass=NestedClassMetaclass):
....:     class Inner:
....:         def __init__(self, *args):
....:             print(args)
....:     def f(self, *args):
....:         print("{} {}".format(self, args))
....:     @staticmethod
....:     def f../_static(*args):
....:         print(args)

sage: outer = Outer()

By default, when Inner is a class nested in Outer, accessing outer.Inner returns the Inner class as is:

sage: outer.Inner is Outer.Inner

In particular, outer is completely ignored in the following call:

sage: x = outer.Inner(1,2,3)
(1, 2, 3)

This is similar to what happens with a static method:

sage: outer.f../_static(1,2,3)
(1, 2, 3)

In some cases, we would want instead Inner to receive outer as parameter, like in a usual method call:

sage: outer.f(1,2,3)
<__main__.Outer object at ...> (1, 2, 3)

To this end, outer.f returns a bound method:

sage: outer.f
<bound method Outer.f of <__main__.Outer object at ...>>

so that outer.f(1,2,3) is equivalent to:

sage: Outer.f(outer, 1,2,3)
<__main__.Outer object at ...> (1, 2, 3)

BindableClass gives this binding behavior to all its subclasses:

sage: from sage.misc.bindable_class import BindableClass
sage: class Outer(metaclass=NestedClassMetaclass):
....:     class Inner(BindableClass):
....:         " some documentation "
....:         def __init__(self, outer, *args):
....:             print("{} {}".format(outer, args))

Calling Outer.Inner returns the (unbound) class as usual:

sage: Outer.Inner
<class '__main__.Outer.Inner'>

However, outer.Inner(1,2,3) is equivalent to Outer.Inner(outer, 1,2,3):

sage: outer = Outer()
sage: x = outer.Inner(1,2,3)
<__main__.Outer object at ...> (1, 2, 3)

To achieve this, outer.Inner returns (some sort of) bound class:

sage: outer.Inner
<bound class '__main__.Outer.Inner' of <__main__.Outer object at ...>>


This is not actually a class, but an instance of functools.partial:

sage: type(outer.Inner).mro()
[<class 'sage.misc.bindable_class.BoundClass'>,
 <... 'functools.partial'>,
 <... 'object'>]

Still, documentation works as usual:

sage: outer.Inner.__doc__
' some documentation '
class sage.misc.bindable_class.BoundClass(*args)

Bases: functools.partial

class sage.misc.bindable_class.Inner2

Bases: sage.misc.bindable_class.BindableClass

Some documentation for Inner2

class sage.misc.bindable_class.Outer

Bases: object

A class with a bindable nested class, for testing purposes

class Inner

Bases: sage.misc.bindable_class.BindableClass

Some documentation for Outer.Inner


alias of Inner2