From 9f6149d07a99fe467c4322b60760c706db3f2ebd Mon Sep 17 00:00:00 2001 From: Praise Tompane Date: Thu, 27 Mar 2025 16:37:04 +0200 Subject: [PATCH 1/2] docs: abstract base classes and container abstract base classes --- ..._def.txt => 0_python_runtime_services.txt} | 3 ++- .../29_python_runtime_services/9_abcs.txt | 23 ++++++++++++++++ .../5_iterator_types/1_iterator.txt | 2 +- .../5_iterator_types/2_generator.txt | 2 +- .../3_generator_expression.txt | 2 +- .../0_collections_abcs.txt | 27 +++++++++++++++++++ 6 files changed, 55 insertions(+), 4 deletions(-) rename 2_standard_library/29_python_runtime_services/{0_def.txt => 0_python_runtime_services.txt} (69%) create mode 100755 2_standard_library/29_python_runtime_services/9_abcs.txt create mode 100644 2_standard_library/8_data_types/5_collections_abstract_base_classes/0_collections_abcs.txt diff --git a/2_standard_library/29_python_runtime_services/0_def.txt b/2_standard_library/29_python_runtime_services/0_python_runtime_services.txt similarity index 69% rename from 2_standard_library/29_python_runtime_services/0_def.txt rename to 2_standard_library/29_python_runtime_services/0_python_runtime_services.txt index 23cb54f..236899b 100644 --- a/2_standard_library/29_python_runtime_services/0_def.txt +++ b/2_standard_library/29_python_runtime_services/0_python_runtime_services.txt @@ -15,4 +15,5 @@ def python_runtime_services: - proof: ??? -References: ??? +References: + The Python Standard Library. Python Runtime Services. 2025. https://docs.python.org/3/library/python.html diff --git a/2_standard_library/29_python_runtime_services/9_abcs.txt b/2_standard_library/29_python_runtime_services/9_abcs.txt new file mode 100755 index 0000000..e47d9df --- /dev/null +++ b/2_standard_library/29_python_runtime_services/9_abcs.txt @@ -0,0 +1,23 @@ +def abcs: + - formal: ??? + + - in words: ??? + + - plain english: ??? + + - intuition: ??? + + - properties: + - specification: + - https://peps.python.org/pep-3119/ + - https://docs.python.org/3/library/abc.html#module-abc + - implementation: https://github.com/python/cpython/blob/3.13/Lib/abc.py + + - examples: ??? + + - use cases: ??? + + - proof: ??? + +References: + The Python Standard Library. Python Runtime Services. 2025. https://docs.python.org/3/library/python.html diff --git a/2_standard_library/4_built_in_types/5_iterator_types/1_iterator.txt b/2_standard_library/4_built_in_types/5_iterator_types/1_iterator.txt index 8c068f6..9ade1af 100644 --- a/2_standard_library/4_built_in_types/5_iterator_types/1_iterator.txt +++ b/2_standard_library/4_built_in_types/5_iterator_types/1_iterator.txt @@ -26,5 +26,5 @@ def iterator: - proof: None. It is a definition. References: - https://docs.python.org/3.11/glossary.html#term-iterator + The Python Standard Library. 2025. https://docs.python.org/3/glossary.html#term-iterator diff --git a/2_standard_library/4_built_in_types/5_iterator_types/2_generator.txt b/2_standard_library/4_built_in_types/5_iterator_types/2_generator.txt index 85b359f..678ae1f 100644 --- a/2_standard_library/4_built_in_types/5_iterator_types/2_generator.txt +++ b/2_standard_library/4_built_in_types/5_iterator_types/2_generator.txt @@ -21,6 +21,6 @@ def generator | generator function: - proof: None. It is a definition. References: - https://docs.python.org/3.11/glossary.html#term-generator + The Python Standard Library. 2025. https://docs.python.org/3/glossary.html#term-generator diff --git a/2_standard_library/4_built_in_types/5_iterator_types/3_generator_expression.txt b/2_standard_library/4_built_in_types/5_iterator_types/3_generator_expression.txt index 7b7a2dd..9957600 100644 --- a/2_standard_library/4_built_in_types/5_iterator_types/3_generator_expression.txt +++ b/2_standard_library/4_built_in_types/5_iterator_types/3_generator_expression.txt @@ -19,5 +19,5 @@ - proof: None. It is a definition. References: - https://docs.python.org/3.11/glossary.html#term-generator-iterator + The Python Standard Library. 2025. https://docs.python.org/3/glossary.html#term-generator-iterator diff --git a/2_standard_library/8_data_types/5_collections_abstract_base_classes/0_collections_abcs.txt b/2_standard_library/8_data_types/5_collections_abstract_base_classes/0_collections_abcs.txt new file mode 100644 index 0000000..dc828fc --- /dev/null +++ b/2_standard_library/8_data_types/5_collections_abstract_base_classes/0_collections_abcs.txt @@ -0,0 +1,27 @@ +def collections_abcs: + - formal: ??? + + - in words: ??? + + - plain english: ??? + + - intuition: ??? + + - properties: + - implementation: https://github.com/python/cpython/tree/3.11/Lib/_collections_abc.py + - specification: https://docs.python.org/3/library/collections.abc.html + - examples: + - test interface compliance: + - hashable + - iterable + - mapping + - ... + + - use cases: + - implement custom classes that satisfy the container API. + - test compliance to a collections' interface using `issubclass` or `isinstance`. + + - proof: ??? + +References: + The Python Standard Library. 2025. collections.abc — Abstract Base Classes for Containers, Data Types. https://docs.python.org/3/library/collections.abc.html From 0eb7f4d02f81d7f7a3dec2bfd3e62a1f0e5ab9da Mon Sep 17 00:00:00 2001 From: Praise Tompane Date: Fri, 28 Mar 2025 11:14:09 +0200 Subject: [PATCH 2/2] fix: ABC and ABCMeta usecase and abc module decorator. --- .spellcheck_exceptions_dictionary.txt | 6 ++- .../29_python_runtime_services/9_abcs.txt | 6 ++- .../9_abcs/1_abc_meta/app.py | 20 +++++++++ .../9_abcs/1_abc_meta/interfaces/__init__.py | 0 .../9_abcs/1_abc_meta/interfaces/s_one.py | 22 ++++++++++ .../9_abcs/1_abc_meta/interfaces/s_two.py | 9 ++++ .../9_abcs/1_abc_meta/scenario.txt | 12 +++++ .../9_abcs/1_abc_meta/thirdparty/__init__.py | 0 .../thirdparty/third_party_class.py | 3 ++ .../9_abcs/1_abc_meta/workers/__init__.py | 0 .../9_abcs/1_abc_meta/workers/worker.py | 5 +++ .../2_decorators/TestClassMethodDecorator.py | 44 +++++++++++++++++++ 12 files changed, 125 insertions(+), 2 deletions(-) create mode 100644 4_experiments/2_standard_library/29_python_runtime_services/9_abcs/1_abc_meta/app.py create mode 100644 4_experiments/2_standard_library/29_python_runtime_services/9_abcs/1_abc_meta/interfaces/__init__.py create mode 100644 4_experiments/2_standard_library/29_python_runtime_services/9_abcs/1_abc_meta/interfaces/s_one.py create mode 100644 4_experiments/2_standard_library/29_python_runtime_services/9_abcs/1_abc_meta/interfaces/s_two.py create mode 100644 4_experiments/2_standard_library/29_python_runtime_services/9_abcs/1_abc_meta/scenario.txt create mode 100644 4_experiments/2_standard_library/29_python_runtime_services/9_abcs/1_abc_meta/thirdparty/__init__.py create mode 100644 4_experiments/2_standard_library/29_python_runtime_services/9_abcs/1_abc_meta/thirdparty/third_party_class.py create mode 100644 4_experiments/2_standard_library/29_python_runtime_services/9_abcs/1_abc_meta/workers/__init__.py create mode 100644 4_experiments/2_standard_library/29_python_runtime_services/9_abcs/1_abc_meta/workers/worker.py create mode 100644 4_experiments/2_standard_library/29_python_runtime_services/9_abcs/2_decorators/TestClassMethodDecorator.py diff --git a/.spellcheck_exceptions_dictionary.txt b/.spellcheck_exceptions_dictionary.txt index ceeceab..d82e23c 100644 --- a/.spellcheck_exceptions_dictionary.txt +++ b/.spellcheck_exceptions_dictionary.txt @@ -52,7 +52,11 @@ userspace userguide sdist exe - +hashable +checksubclass +issubclasshook +issubclass +metaclass # python keywords pprint diff --git a/2_standard_library/29_python_runtime_services/9_abcs.txt b/2_standard_library/29_python_runtime_services/9_abcs.txt index e47d9df..d840d9d 100755 --- a/2_standard_library/29_python_runtime_services/9_abcs.txt +++ b/2_standard_library/29_python_runtime_services/9_abcs.txt @@ -11,11 +11,15 @@ def abcs: - specification: - https://peps.python.org/pep-3119/ - https://docs.python.org/3/library/abc.html#module-abc + - implementation: https://github.com/python/cpython/blob/3.13/Lib/abc.py + - examples: ??? - - use cases: ??? + - use cases: + - register a third-party class as a subclass of our own abstract class(i.e. metaclass) + this enables using the third-party class in polymorphic code as a subclass. - proof: ??? diff --git a/4_experiments/2_standard_library/29_python_runtime_services/9_abcs/1_abc_meta/app.py b/4_experiments/2_standard_library/29_python_runtime_services/9_abcs/1_abc_meta/app.py new file mode 100644 index 0000000..cd4188d --- /dev/null +++ b/4_experiments/2_standard_library/29_python_runtime_services/9_abcs/1_abc_meta/app.py @@ -0,0 +1,20 @@ +from workers.worker import Worker +from thirdparty.third_party_class import ThirdPartyWorker +from interfaces.s_one import SOneContract +from interfaces.s_two import STwoContract + +if __name__ == "__main__": + + """ + This is the registration of the third party to enable our system to treat it as a subclass of SOneContract". + This makes ThirdPartyWorker a virtual subclass of SOneContract + """ + SOneContract.register(ThirdPartyWorker) + + workers = [Worker(), ThirdPartyWorker()] + + for worker in workers: + if(isinstance(worker, SOneContract)): + worker.do_work() + if(isinstance(worker, STwoContract)): + worker.s_two_work() \ No newline at end of file diff --git a/4_experiments/2_standard_library/29_python_runtime_services/9_abcs/1_abc_meta/interfaces/__init__.py b/4_experiments/2_standard_library/29_python_runtime_services/9_abcs/1_abc_meta/interfaces/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/4_experiments/2_standard_library/29_python_runtime_services/9_abcs/1_abc_meta/interfaces/s_one.py b/4_experiments/2_standard_library/29_python_runtime_services/9_abcs/1_abc_meta/interfaces/s_one.py new file mode 100644 index 0000000..e192f0d --- /dev/null +++ b/4_experiments/2_standard_library/29_python_runtime_services/9_abcs/1_abc_meta/interfaces/s_one.py @@ -0,0 +1,22 @@ +from abc import ABCMeta, abstractmethod + +class SOneContract(metaclass = ABCMeta): + + @abstractmethod + def do_work(self): + raise NotImplemented + + """ + # NB: @abstractmethod only applies to subclasses created using regular inheritance. + virtual sub classes can be instantiated without implementing it, while regular subclasses cannot. + @abstractmethod + def s_one_specialty(self): + raise NotImplemented + """ + + def __issubclasshook__(cls): + """ + NB: Alternativeltm we overload the behvaiour of `issubclass` function for SOneContract + and define what it means to be a SOneContract. + """ + pass \ No newline at end of file diff --git a/4_experiments/2_standard_library/29_python_runtime_services/9_abcs/1_abc_meta/interfaces/s_two.py b/4_experiments/2_standard_library/29_python_runtime_services/9_abcs/1_abc_meta/interfaces/s_two.py new file mode 100644 index 0000000..6273a12 --- /dev/null +++ b/4_experiments/2_standard_library/29_python_runtime_services/9_abcs/1_abc_meta/interfaces/s_two.py @@ -0,0 +1,9 @@ +from abc import ABC + +class STwoContract(ABC): + """ + NB: This is an alternative to explicitly setting the metaclass to ABCMeta. + The ABC class is syntactic sugar. + """ + def s_two_work(self): + pass \ No newline at end of file diff --git a/4_experiments/2_standard_library/29_python_runtime_services/9_abcs/1_abc_meta/scenario.txt b/4_experiments/2_standard_library/29_python_runtime_services/9_abcs/1_abc_meta/scenario.txt new file mode 100644 index 0000000..d8c401d --- /dev/null +++ b/4_experiments/2_standard_library/29_python_runtime_services/9_abcs/1_abc_meta/scenario.txt @@ -0,0 +1,12 @@ +two teams: + - internal classes that implement some contract + - immutable third party provided classes that implement some contract + +issues: + - we have polymorphic code that delegates to work workers, based on the contract they implement, that cannot be checked with duck typing. + - solution: + - register the third party classes as a subclass using Python's ABCs. + - implement custom behaviour for `issubclass` for a specific ABC, using `__issubclasshook__()`. + - __issubclasshook__() is used by __checksubclass__() + + diff --git a/4_experiments/2_standard_library/29_python_runtime_services/9_abcs/1_abc_meta/thirdparty/__init__.py b/4_experiments/2_standard_library/29_python_runtime_services/9_abcs/1_abc_meta/thirdparty/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/4_experiments/2_standard_library/29_python_runtime_services/9_abcs/1_abc_meta/thirdparty/third_party_class.py b/4_experiments/2_standard_library/29_python_runtime_services/9_abcs/1_abc_meta/thirdparty/third_party_class.py new file mode 100644 index 0000000..b92ce1d --- /dev/null +++ b/4_experiments/2_standard_library/29_python_runtime_services/9_abcs/1_abc_meta/thirdparty/third_party_class.py @@ -0,0 +1,3 @@ +class ThirdPartyWorker: + def do_work(self): + print("I am a third party class doing work") \ No newline at end of file diff --git a/4_experiments/2_standard_library/29_python_runtime_services/9_abcs/1_abc_meta/workers/__init__.py b/4_experiments/2_standard_library/29_python_runtime_services/9_abcs/1_abc_meta/workers/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/4_experiments/2_standard_library/29_python_runtime_services/9_abcs/1_abc_meta/workers/worker.py b/4_experiments/2_standard_library/29_python_runtime_services/9_abcs/1_abc_meta/workers/worker.py new file mode 100644 index 0000000..750c1da --- /dev/null +++ b/4_experiments/2_standard_library/29_python_runtime_services/9_abcs/1_abc_meta/workers/worker.py @@ -0,0 +1,5 @@ +from interfaces.s_one import SOneContract + +class Worker(SOneContract): + def do_work(self): + print("I am an internal worker doing work") \ No newline at end of file diff --git a/4_experiments/2_standard_library/29_python_runtime_services/9_abcs/2_decorators/TestClassMethodDecorator.py b/4_experiments/2_standard_library/29_python_runtime_services/9_abcs/2_decorators/TestClassMethodDecorator.py new file mode 100644 index 0000000..d686564 --- /dev/null +++ b/4_experiments/2_standard_library/29_python_runtime_services/9_abcs/2_decorators/TestClassMethodDecorator.py @@ -0,0 +1,44 @@ +import abc +from abc import abstractmethod + +class TestClassMethodDecorator(): + @staticmethod + def method_static_method(): + print("In static method") + + @classmethod + def method_class_method(cls): + print("In class method") + + @abstractmethod + def method_abstract(self): + print("In abstract method") + + @staticmethod + @abstractmethod + def method_abstract_static(): + print("In abstract static method") + + def invoke_method_static_method_in_class(self): + """ + Q: why can't we do this? + """ + method_static_method() + + +if __name__ == "__main__": + print(f"Test: {TestClassMethodDecorator.__name__}") + a = TestClassMethodDecorator() + a.method_static_method() + TestClassMethodDecorator.method_static_method() + # a.invoke_method_static_method_in_class() + + a.method_abstract() + # TestClassMethodDecorator.method_abstract() + TestClassMethodDecorator.method_abstract_static() + + a.method_class_method() + TestClassMethodDecorator.method_class_method() + + print(abc.get_cache_token()) + print(type(abc.get_cache_token())) \ No newline at end of file