[Python-3000] comments

tomer filiba tomerfiliba at gmail.com
Sun May 6 19:03:43 CEST 2007


i finished reading almost all of the new peps, so to prevent cluttering
i'll post all my comments in a single message.


3130 (Access to Current Module/Class/Function)
------------------------------------------------
why make them keywords? they could as well be builtin functions,
like globals() and locals(). i.e., getmodule(), getclass(), and
getfunction(). these functions will just climb up the stack frames
until they find what you're asking for.

also -- the class object is constructed only AFTER the code
of the class has finished executing, meaning getclass()
or __thisclass__ will not work at class level.

so the class mechanism needs to be changed as well.


3119 (Introducing Abstract Base Classes)
3141 (A Type Hierarchy for Numbers)
------------------------------------------------
these two are very closely related, so i'll treat them as one.
first, being able to override isinstance and issubclass is a
great addition. it would make proxies much more transparent,
and i could remove half of the black magic code from my RPC lib.

other than that -- it would be horrible -- as i'll now explain.
first, about the algebraic structures, such as fields, rings
and what not -- not all of us are mathematicians.

when john doe wants to write HisLeetNumber, i doubt he'll
be able understand all of the subtle differences. adding two
numbers does not require one to take Algebra 101.

second -- i have stressed that before but i hope this time it
may sound more convincing -- a type hierarchy is a foolish concept
that is not strong enough to convey *all* of the constraints one
may want to express, while being very rigid and *static*.
PJE's proposal seems the only suitable one, imho.

sure, duck typing by itself is not powerful enough to allow
constraints and adaptation -- but a new type hierarchy
is not gonna solve these issues.

to start with, it's python after all, not some statically compiled
type-checking langauge. i can still derive from Set and change
the signature of a method (def __len__(self, foobar)), and break
everything -- even though isinstance would approve. this may happen
because of a "malicious" coder or just by a blunt user.

so i hope this settles the case for "type safety". if you want to be
static, use java.

what you DO want is a way to distinguish between objects that "look
similar" to others. for instance, sequences and mappings both
"look similar" -- having __getitem__, __len__, __contains__,
and so on.

what you want is a way to say "this class is a sequence".
you can do that by inheriting from some BaseSequence, but sooner
or later, you'll end up with classes that derive from 10 different
bases, and maintaining such a class hierarchy will become very
time consuming and bug-prone.

besides, imagine that someone wrote his own sequence class, which
does not inherit from BaseSequence, but is otherwise a perfectly
good sequence. still -- you will not be able to use it, as isinstance
checks will fail. manually patching the MRO is impossible, and so
you have to start finding workarounds.

the solution, imo, would be in the form of contracts. the contract
is just a class that defines the interface, and documents how it
should behave. for instance, whether __add__ is commutative, etc.

by itself is may sound just like abstract base classes, but the
difference  is your class won't inherit from them -- rather it
would state it *conforms* with them.

class MappingContract:
    implements(ContainerContract)

    def __getitem__(self, key):
        """if key is not found, raises KeyError"""
    def get(self, key, default = None):
        """returns self[key] if this does not raise KeyError,
         and the default value if it does"""
    def __contains__(self, key):
        """tests if the key exists in the mapping"""

class LeetDict:
    implements(MappingContract)
    implements(SomeOtherContract)

    def ...

ld = LeetDict()
isimplementing(ld, MappingContract) # True
isimplementing(ld, ContainerContract) # True
isimplementing(ld, SequenceContract) # False

this way, you'll never have conflicting base classes/metaclasses,
and still be able to express any functionality that you'll ever
want. again, with ABCs, classes would grow very large inheritance
trees, that at some point are bound to conflict/collide.

moreover, contracts are more "declarative". LeetDict declares
it complies to some contract, rather than forcing it to have statically
inherited that contract as an ABC. we can, if the need arises, patch
a third-party class by declaring it complies with some contract,
which is in fact unknown to the third-party class.

this approach is also more extensible than ABCs:
* a metaclass/class decorator can be used to check at class
  creation time that all of the contracts are satisfied, etc.
* the contract may be any object. even just a big string that
  describes the contract textually
* but it may also be possible to describe complex requirements
  expressively with decorators; for example:

        class Number:
            @commutative
            @associative
            def __add__(self, other):
                "returns self + other"

allowing you to specify individual "properties" to each member
of the contract, so you don't have to know about fields and rings
just to implement an associative operation.

still, the contracts approach has no trouble tackling the suggested
Fields/Rings/Monads classification, should one desire to.


3129, 3127, 3177
------------------------------------------------
as for 3129 (Class Decorators) and 3127 (Integer Literal Support
and Syntax) -- it's about time we have these. and btw, the status
of pep 3117 ought to be changed to 'accepted'... it would have
more impact that way :)


-tomer


More information about the Python-3000 mailing list