Hello Python devs, As a regular Python user, I find the abc module useful for making Python's duck typing more explicit. In particular, I ofen use it like a Java interface or C header, to provide methods to implement for a given "duck type". 90% of the time, it ends up looking something like this: class Foo(metaclass=abc.ABCMeta): @abc.abstractmethod def f1(self): raise NotImplementedError @staticmethod @abc.abstractmethod def f2(arg1): raise NotImplementedError ... What if there was a function, say make_abstract_method (better name pending), so that the above could be written like: class Foo(metaclass=abc.ABCMeta): f1 = abc.make_abstract_method('f1', ['self']) f2 = staticmethod(abc.make_abstract_method('f2', ['arg1'])) ... I think that it would make ABC definitions a lot more compact for many use cases, but I welcome any criticisms against this idea. Allen Li
How would you get the docstrings in? It seems cramming that much on a
single line doesn't help readability (even though I agree there is a
fair amount of boilerplace).
On Thu, Dec 5, 2013 at 10:20 AM, Allen Li
Hello Python devs,
As a regular Python user, I find the abc module useful for making Python's duck typing more explicit. In particular, I ofen use it like a Java interface or C header, to provide methods to implement for a given "duck type".
90% of the time, it ends up looking something like this:
class Foo(metaclass=abc.ABCMeta):
@abc.abstractmethod def f1(self): raise NotImplementedError
@staticmethod @abc.abstractmethod def f2(arg1): raise NotImplementedError
...
What if there was a function, say make_abstract_method (better name pending), so that the above could be written like:
class Foo(metaclass=abc.ABCMeta):
f1 = abc.make_abstract_method('f1', ['self']) f2 = staticmethod(abc.make_abstract_method('f2', ['arg1'])) ...
I think that it would make ABC definitions a lot more compact for many use cases, but I welcome any criticisms against this idea.
Allen Li _______________________________________________ Python-Dev mailing list Python-Dev@python.org https://mail.python.org/mailman/listinfo/python-dev Unsubscribe: https://mail.python.org/mailman/options/python-dev/guido%40python.org
-- --Guido van Rossum (python.org/~guido)
On Thu, Dec 5, 2013 at 1:24 PM, Guido van Rossum
How would you get the docstrings in? It seems cramming that much on a single line doesn't help readability (even though I agree there is a fair amount of boilerplace).
One way to reduce the amount of boilerplate code is to make abstractmethod to supply raise NotImplementedError body when none is given. Then you can write class Foo: @abc.abstractmethod def do_bar(self): """perform bar""" instead of class Foo: @abc.abstractmethod def do_bar(self): """perform bar""" raise NotImplementedError The docstring will be required when skipping the body which is probably a good thing.
On 12/05/2013 10:56 AM, Alexander Belopolsky wrote:
On Thu, Dec 5, 2013 at 1:24 PM, Guido van Rossum wrote:
How would you get the docstrings in? [...]
One way to reduce the amount of boilerplate code is to make abstractmethod to supply raise NotImplementedError body when none is given. Then you can write
class Foo: @abc.abstractmethod def do_bar(self): """perform bar"""
The docstring will be required when skipping the body which is probably a good thing.
How will abstractmethod know its function has no body? -- ~Ethan~
On Thu, Dec 5, 2013 at 2:22 PM, Ethan Furman
On 12/05/2013 10:56 AM, Alexander Belopolsky wrote:
On Thu, Dec 5, 2013 at 1:24 PM, Guido van Rossum wrote:
How would you get the docstrings in? [...]
One way to reduce the amount of boilerplate code is to make abstractmethod to supply raise NotImplementedError body when none is given. Then you can write
class Foo: @abc.abstractmethod def do_bar(self): """perform bar"""
The docstring will be required when skipping the body which is probably a good thing.
How will abstractmethod know its function has no body?
Technically you could inspect the code object of the method. to figure out if the body is empty. But I would be very leery of this being a default behaviour. Raising NotImplementedError is not necessarily what the default result should be for a method. If you are building a class that supports multiple inheritance you will be calling super().do_bar() almost blindly which, if you are not careful, will raise NotImplementedError and that might not be appropriate. Maybe returning None is more reasonable, maybe raising TypeError. But support a default blindly like this can promote bad practices. You can see all of changes I had to make for importlib.abc in Python 3.4 to have API-matching default exceptions and return values instead of blindly raising NotImplementedError as a lesson learned: http://docs.python.org/3.4/library/importlib.html#module-importlib.abc. Hunt down Thomas Wouters at PyCon if you want to hear the same arguments he gave me as to why blindly raise NotImplementedError is bad.
On Thu, Dec 5, 2013 at 3:06 PM, Brett Cannon
How will abstractmethod know its function has no body?
Technically you could inspect the code object of the method. to figure out if the body is empty.
One way if to check f.__code__.co_lnotab - it will be empty when f has no body.
On 05/12/2013 19:22, Ethan Furman wrote:
On 12/05/2013 10:56 AM, Alexander Belopolsky wrote:
On Thu, Dec 5, 2013 at 1:24 PM, Guido van Rossum wrote:
How would you get the docstrings in? [...]
One way to reduce the amount of boilerplate code is to make abstractmethod to supply raise NotImplementedError body when none is given. Then you can write
class Foo: @abc.abstractmethod def do_bar(self): """perform bar"""
The docstring will be required when skipping the body which is probably a good thing.
How will abstractmethod know its function has no body?
An abstract method won't have a body (I'm not counting the docstring). If it _does_ have a body, then it's _not_ an abstract method!
On 12/05/2013 12:39 PM, MRAB wrote:
On 05/12/2013 19:22, Ethan Furman wrote:
On 12/05/2013 10:56 AM, Alexander Belopolsky wrote:
On Thu, Dec 5, 2013 at 1:24 PM, Guido van Rossum wrote:
How would you get the docstrings in? [...]
One way to reduce the amount of boilerplate code is to make abstractmethod to supply raise NotImplementedError body when none is given. Then you can write
class Foo: @abc.abstractmethod def do_bar(self): """perform bar"""
The docstring will be required when skipping the body which is probably a good thing.
How will abstractmethod know its function has no body?
An abstract method won't have a body (I'm not counting the docstring).
If it _does_ have a body, then it's _not_ an abstract method!
To quote the docs [1]:
Note
Unlike Java abstract methods, these abstract methods may have an implementation. This implementation can be called via the super() mechanism from the class that overrides it. This could be useful as an end-point for a super-call in a framework that uses cooperative multiple-inheritance.
-- ~Ethan~ [1] http://docs.python.org/dev/library/abc.html
On Thu, Dec 05, 2013 at 10:24:11AM -0800, Guido van Rossum wrote:
How would you get the docstrings in? It seems cramming that much on a single line doesn't help readability (even though I agree there is a fair amount of boilerplace).
I was basing my initial suggestion somewhat on collections.namedtuple,
which doesn't support docstring either. One option would be to simply
not allow for docstrings, requiring the boilerplate method, the other
would be to add a parameter for the docstring:
make_abstractmethod(name, arglist, docstring='')
On Thu, Dec 5, 2013 at 2:22 PM, Ethan Furman
On 12/05/2013 10:56 AM, Alexander Belopolsky wrote:
On Thu, Dec 5, 2013 at 1:24 PM, Guido van Rossum wrote:
How would you get the docstrings in? [...]
One way to reduce the amount of boilerplate code is to make abstractmethod to supply raise NotImplementedError body when none is given. Then you can write
class Foo: @abc.abstractmethod def do_bar(self): """perform bar"""
The docstring will be required when skipping the body which is probably a good thing.
How will abstractmethod know its function has no body?
I don't think this is a good idea at all, very magical behavior. It is not intuitive or explicit that an abstractmethod-decorated empty function raises NotImplementedError. On Thu, Dec 05, 2013 at 03:06:00PM -0500, Brett Cannon wrote:
But I would be very leery of this being a default behaviour. Raising NotImplementedError is not necessarily what the default result should be for a method. If you are building a class that supports multiple inheritance you will be calling super().do_bar() almost blindly which, if you are not careful, will raise NotImplementedError and that might not be appropriate. Maybe returning None is more reasonable, maybe raising TypeError. But support a default blindly like this can promote bad practices.
I'm not sure what's considered best practice with super() at the moment. super() is dangerous if you don't know what you are doing in multi-inheritance design, so I'd consider calling super() blindly is the problem here, not raising NotImplementedError. I do think raising NotImplementedError is reasonable default behavior for an abstractmethod. The only two alternatives are doing nothing/pass/return None or having actual code in the method. The former is only useful to silently ignore blind super() calling, the latter you would define and decorate the method normally. On Thu, Dec 05, 2013 at 08:39:05PM +0000, MRAB wrote:
An abstract method won't have a body (I'm not counting the docstring).
If it _does_ have a body, then it's _not_ an abstract method!
It could have a body, though I would agree it's not commonly used that way. Maybe something like the following (though it's a poor example): class Printer(metaclass=abc.ABCMeta): @abc.abstractmethod def print(self): print("Print done.") class Foo(Printer): def print(self): print('foo') super().print()
On Thu, Dec 5, 2013 at 1:12 PM, Allen Li
On Thu, Dec 05, 2013 at 10:24:11AM -0800, Guido van Rossum wrote:
How would you get the docstrings in? It seems cramming that much on a single line doesn't help readability (even though I agree there is a fair amount of boilerplace).
I was basing my initial suggestion somewhat on collections.namedtuple, which doesn't support docstring either.
Not yet. :-) (http://bugs.python.org/issue16669)
One option would be to simply not allow for docstrings, requiring the boilerplate method, the other would be to add a parameter for the docstring:
make_abstractmethod(name, arglist, docstring='')
On Thu, Dec 5, 2013 at 2:22 PM, Ethan Furman
wrote: On 12/05/2013 10:56 AM, Alexander Belopolsky wrote:
On Thu, Dec 5, 2013 at 1:24 PM, Guido van Rossum wrote:
How would you get the docstrings in? [...]
One way to reduce the amount of boilerplate code is to make abstractmethod to supply raise NotImplementedError body when none is given. Then you can write
class Foo: @abc.abstractmethod def do_bar(self): """perform bar"""
The docstring will be required when skipping the body which is probably a good thing.
How will abstractmethod know its function has no body?
I don't think this is a good idea at all, very magical behavior. It is not intuitive or explicit that an abstractmethod-decorated empty function raises NotImplementedError.
Agreed.
On Thu, Dec 05, 2013 at 03:06:00PM -0500, Brett Cannon wrote:
But I would be very leery of this being a default behaviour. Raising NotImplementedError is not necessarily what the default result should be for a method. If you are building a class that supports multiple inheritance you will be calling super().do_bar() almost blindly which, if you are not careful, will raise NotImplementedError and that might not be appropriate. Maybe returning None is more reasonable, maybe raising TypeError. But support a default blindly like this can promote bad practices.
I'm not sure what's considered best practice with super() at the moment. super() is dangerous if you don't know what you are doing in multi-inheritance design, so I'd consider calling super() blindly is the problem here, not raising NotImplementedError.
Actually I think the combination of not knowing what you're doing and mulitple inheritance is the problem.
I do think raising NotImplementedError is reasonable default behavior for an abstractmethod.
When not using multiple inheritance, yes.
The only two alternatives are doing nothing/pass/return None or having actual code in the method.
The former is only useful to silently ignore blind super() calling, the latter you would define and decorate the method normally.
Actually if you want to support multiple inheritance of your ABC, your abstract methods *must* be no-ops (or have some kind of default behavior that can always be done last).
On Thu, Dec 05, 2013 at 08:39:05PM +0000, MRAB wrote:
An abstract method won't have a body (I'm not counting the docstring).
If it _does_ have a body, then it's _not_ an abstract method!
It could have a body, though I would agree it's not commonly used that way. Maybe something like the following (though it's a poor example):
class Printer(metaclass=abc.ABCMeta):
@abc.abstractmethod def print(self): print("Print done.")
class Foo(Printer):
def print(self): print('foo') super().print()
That's exactly right. -- --Guido van Rossum (python.org/~guido)
On 12/05/2013 10:20 AM, Allen Li wrote:
90% of the time, it ends up looking something like this:
class Foo(metaclass=abc.ABCMeta):
@abc.abstractmethod def f1(self): raise NotImplementedError
@staticmethod @abc.abstractmethod def f2(arg1): raise NotImplementedError
I think we're getting sidetracked by the `raise NotImplementedError` -- why do you have that line in there? If I understand the ABCs correctly a class that does *not* have concrete methods to replace the abstract methods will raise an exception at class creation time, so why do you need the `raise NotImplementedError`? It would only ever happen on a super() type of call. Having said all that, I would hope that any abstract class I had to implement would have good doc strings, and the multi-line format is much easier to read. -1 on the one-liner. -- ~Ethan~
participants (6)
-
Alexander Belopolsky
-
Allen Li
-
Brett Cannon
-
Ethan Furman
-
Guido van Rossum
-
MRAB