Add check_methods function to standard library
Hello, In _collections_abc.py is a private function titled `_check_methods`. It takes a class and a number of method names (as strings), checks if the class has all of the methods, and returns NotImplemented if any are missing. The code is below: ``` def _check_methods(C, *methods): mro = C.__mro__ for method in methods: for B in mro: if method in B.__dict__: if B.__dict__[method] is None: return NotImplemented break else: return NotImplemented return True ``` This is an incredibly convenient function (referred to as check_methods here on out) for creating abstract base classes, and is much simpler than using `hasattr` for each method you want to check. For example: ```
from abc import ABCMeta # Without check_methods class A(metaclass=ABCMeta): ... @classmethod ... def __subclasshook__(cls, subclass): ... return (hasattr(subclass, 'foo') and ... callable(subclass.foo) and ... hasattr(subclass, 'bar') and ... callable(subclass.bar) or ... NotImplemented) ... # With check_methods class B(metaclass=ABCMeta): ... @classmethod ... def __subclasshook(cls, subclass): ... return check_methods(subclass, 'foo', 'bar') ...
This would be a great function to add to the standard lib, perhaps in
the `abc` module.
One problem with `check_methods` as defined in _collections_abc.py is
that it doesn't check if the name is callable. Also, type hints and
more readable variables may be desirable. The final code, if
implemented, may look something like this:
# In imports section: from typing import Literal def check_methods(Class: type, *methods: str) -> Literal[True, NotImplemented]: """Check if class `Class` has methods `methods`.""" mro = Class.__mro__ for method in methods: for Base in mro: if (attr := getattr(Base, method, None)) is not None: if not callable(attr): return NotImplemented break else: return NotImplemented return True ``` Again, this would be a great function to add to the `abc` module or a similar one. I've proposed this in issue 44941. Another possible implementation may be similar to a dataclass that implements __subclasshook__ for you with the desired method checks. --Finn
Sorry, the formatting was terrible. I copy and pasted it from the original issue without really thinking about it. Here's the same thing, but actually readable: In _collections_abc.py is a private function titled _check_methods(). It takes a class and a number of method names (as strings), checks if the class has all of the methods, and returns NotImplemented if any are missing. The code is below: def _check_methods(C, *methods): mro = C.__mro__ for method in methods: for B in mro: if method in B.__dict__: if B.__dict__[method] is None: return NotImplemented break else: return NotImplemented return True This is an incredibly convenient function (referred to as check_methods here on out) for creating abstract base classes, and is much simpler than using hasattr for each method you want to check. For example: from abc import ABCMeta # Without check_methods class A(metaclass=ABCMeta): @classmethod def __subclasshook__(cls, subclass): return (hasattr(subclass, 'foo') and callable(subclass.foo) and hasattr(subclass, 'bar') and callable(subclass.bar) or NotImplemented) # With check_methods class B(metaclass=ABCMeta): @classmethod def __subclasshook(cls, subclass): return check_methods(subclass, 'foo', 'bar') This would be a great function to add to the standard lib, perhaps in the abc module. One problem with check_methods() as defined in _collections_abc.py is that it doesn't check if the name is callable. Also, type hints and more readable variables may be desirable. The final code, if implemented, may look something like this: # In imports section: from typing import Literal def check_methods(Class: type, *methods: str) -> Literal[True, NotImplemented]: """Check if class `Class` has methods `methods`.""" mro = Class.__mro__ for method in methods: for Base in mro: if (attr := getattr(Base, method, None)) is not None: if not callable(attr): return NotImplemented break else: return NotImplemented return True Again, this would be a great function to add to the `abc` module or a similar one. I've proposed this in issue 44941. Another possible implementation may be similar to a dataclass that implements __subclasshook__ for you with the desired method checks. -- Finn
Hello, This to thread has gotten no responses at all, so: Ping! On Sun, Aug 22, 2021, 3:16 PM Finn Mason <finnjavier08@gmail.com> wrote:
Sorry, the formatting was terrible. I copy and pasted it from the original issue without really thinking about it. Here's the same thing, but actually readable:
In _collections_abc.py is a private function titled _check_methods(). It takes a class and a number of method names (as strings), checks if the class has all of the methods, and returns NotImplemented if any are missing. The code is below:
def _check_methods(C, *methods): mro = C.__mro__ for method in methods: for B in mro: if method in B.__dict__: if B.__dict__[method] is None: return NotImplemented break else: return NotImplemented return True
This is an incredibly convenient function (referred to as check_methods here on out) for creating abstract base classes, and is much simpler than using hasattr for each method you want to check. For example:
from abc import ABCMeta # Without check_methods class A(metaclass=ABCMeta): @classmethod def __subclasshook__(cls, subclass): return (hasattr(subclass, 'foo') and callable(subclass.foo) and hasattr(subclass, 'bar') and callable(subclass.bar) or NotImplemented)
# With check_methods class B(metaclass=ABCMeta): @classmethod def __subclasshook(cls, subclass): return check_methods(subclass, 'foo', 'bar')
This would be a great function to add to the standard lib, perhaps in the abc module. One problem with check_methods() as defined in _collections_abc.py is that it doesn't check if the name is callable. Also, type hints and more readable variables may be desirable. The final code, if implemented, may look something like this:
# In imports section: from typing import Literal def check_methods(Class: type, *methods: str) -> Literal[True, NotImplemented]: """Check if class `Class` has methods `methods`.""" mro = Class.__mro__ for method in methods: for Base in mro: if (attr := getattr(Base, method, None)) is not None: if not callable(attr): return NotImplemented break else: return NotImplemented return True
Again, this would be a great function to add to the `abc` module or a similar one. I've proposed this in issue 44941. Another possible implementation may be similar to a dataclass that implements __subclasshook__ for you with the desired method checks.
-- Finn
participants (1)
-
Finn Mason