The technique Eric suggests is probably better than what I had in mind.
But I was thinking you could have an "inherit" decorator for methods (or
for a class as a whole).  It's easy enough for a decorator to attach a
`.__contracts__` attribute to either the class or the individual methods.
Then the decorator(s) in the child can simply look through the `.__mro__`
to find any such parent contracts.  E.g.:


class B(A):
    @inherit_invariants
    def some_method(self, x: int) -> None:
        pass



    @precondition(lambda x: x=42, inherit_parent=True)

    def other_method(self, x: int) -> float:

        return 42/5


I'm not writing a library to do this, so you can tweak the API to be
different than my example.  But this is already well possible.

On the broader idea:

Is there any logical or empirical objection that the design-by-contract is
not useful and hence does not merit to be introduced into the core
language? There is little point in writing a PEP and fleshing out the
details if the community will reject it on grounds that design-by-contract
by itself is meaningless.


Lots of people have explained this relative to lots of ideas, probably
mostly better than I will.  Adding a new feature, even if it is
*technically* backwards compatible, has HUGE costs.  All the
documentation—the books, articles, blog posts, videos, webinars, tutorials,
etc.—about Python has to be updated.  We get a divide between "code that
will work in Python 3.9" versus what will run in 3.7.  The cognitive burden
of learning Python is increased for everyone in the world (millions of
people) because even if they do not use a feature they will encounter code
that does.  There is another section of code in the implementation(s) of
Python that can potentially have bugs and needs to be maintained by someone.

Obviously, design-by-contract is not *meaningless*! It's a specific feature
that is relatively well defined as a concept (and precisely defined in
regard to Eiffel specifically; but we might not implement those *exact*
semantics).  It's also a feature that no languages in particularly
widespread use have decided to implement at the language level.  I've
chatted with Meyer; he's definitely very smart and definitely strongly
opinionated, but I also think he's wrong about the overall importance of
this feature versus lots of others.

In my mind, this feature doesn't come close to meeting the burden of those
high costs listed above (and others I did not mention).  But I don't have
any say in what the core developers will do, beyond in that they might be
influenced by my opinion here.

Yours, David...


On Wed, Aug 29, 2018 at 6:41 PM Eric Fahlgren <ericfahlg...@gmail.com>
wrote:

> On Wed, Aug 29, 2018 at 3:07 PM Marko Ristin-Kaufmann <
> marko.ris...@gmail.com> wrote:
>
>> Could you please elaborate a bit? I don't see how the annotations would
>> make the contracts invoked on inheritance. Consider this simple case:
>>
>> class A:
>>     @icontract.pre(lambda x: x > 0)
>>     def some_method(self, x: int)->None:
>>         pass
>>
>> class B(A):
>>     # Precondition should be inherited here.
>>     def some_method(self, x: int) -> None:
>>         pass
>>
>> You would still need to somehow decorate manually the overridden methods
>> even though you would not specify any new contracts, right? Is there a
>> mechanism in Python that I am not aware of that would allow us to
>> accomplish that?
>>
>
> A metaclass does this pretty easily.  I have a thing I wrote years ago
> called MIS (Multi-Inheritance Safety) that is used to ensure you don't do
> anything stupid in our physics-modeling database.  The database is a
> collection of ~200 Python classes/mixins all deriving madly from each other
> to get various behaviors (Has mass? You'll need this.  Has moments?  You'll
> need this.  Has physical extent, i.e., can be seen?  You'll need these...).
>
> Anyhow, the basemost class has the metaclass, which traverses all the
> methods in the subclasses and makes sure you don't have methods with the
> same name in two distinct superclass trees and such things.  It also forces
> you to be explicit when you overload a method from a base class (among
> other things, but this one is easy to illustrate).
>
> class MIS(type):
>     def __init__(cls, name, bases, namespace):
>         mro = cls.mro()[1:-1]  # All the stuff between new class and
> 'object'
>         for method_name, method in namespace.items():
>             if isinstance(method, executable_types):
>                 if not getattr(method, '_its_ok', False):
>                     # Make sure it's not in any baser class.
>                 # Could easily check for decorated pre/post properties and
> copy them...
>
> def override(func):
>     # Mark the function as a valid override...
>     func._its_ok = True
>     return func
>
> class Base(metaclass=MIS):
>     def something(self):
>         pass
>
> class Derived(Base):
>     @override # Signal that this is ok, otherwise we get an error.
>     def something(self): ...
>
> _______________________________________________
> Python-ideas mailing list
> Python-ideas@python.org
> https://mail.python.org/mailman/listinfo/python-ideas
> Code of Conduct: http://python.org/psf/codeofconduct/
>


-- 
Keeping medicines from the bloodstreams of the sick; food
from the bellies of the hungry; books from the hands of the
uneducated; technology from the underdeveloped; and putting
advocates of freedom in prisons.  Intellectual property is
to the 21st century what the slave trade was to the 16th.
_______________________________________________
Python-ideas mailing list
Python-ideas@python.org
https://mail.python.org/mailman/listinfo/python-ideas
Code of Conduct: http://python.org/psf/codeofconduct/

Reply via email to