Skip to main content

Make any type binary-combinable with a single line of code.

The most obvious use case arguably is the creation of binary expressions over custom types, but bincombo is not limited to combination of boolean values. By providing appropriate map/reduce operations, any type of data of the discrete members of a combination can be aggregated to form a combined result.

The implementation solely relies on inheritance, no meta programming is involved. You are free to use your own metaclasses, should you wish to do so.

Suppose you have a custom type Check, which accepts or rejects values depending on the result of the invocation of some callable. You can make instances of it combinable using the binary operators & (and) and | (or) and also support the unary ~ (invert) by using the combinable() decorator:

import bincombo

@bincombo.combinable(methods=("check",))
class Check:
    __slots__ = ("checker",)

    def __init__(self, checker):
        self.checker = checker

    def check(self, value):
        return self.checker(value)

Now, Check objects can be binary-combined:

c1 = Check(lambda v: isinstance(v, int) and v >= 42)
c2 = Check(lambda v: isinstance(v, str))
c3 = Check(lambda v: "hello" in v)
c = c1 | c2 & ~c3
c.check(41)  # False
c.check(42)  # True
c.check("hello, world!")  # False
c.check("hey, world!")  # True

The combinable() decorator creates a number of types needed to represent discrete checks and combinations thereof. These are stored in a Config object, which is aavailable as class attribute BIN_CONFIG of Check. It can be worth storing these types as module attributes alongside your Check class to have them at hand for explicit use or type checking:

BaseCheck = Check.BIN_CONFIG.base_type
CheckCombo = Check.BIN_CONFIG.combo_type
AllChecks = Check.BIN_CONFIG.and_type
AnyCheck = Check.BIN_CONFIG.or_type

All types in this module have __slots__ defined for smaller memory footprints and improved lookup times, as have the types created by combinable(). It is recommended to also equip your own type with __slots__ if possible to benefit from entirely __dict__-less objects.

To gain a better understanding of how all the types relate or to further customize them, here is how you would make Check binary-combinable without using the combinable() helper:

# This is a base class from which both the discrete Check type and the type
# representing a combination of Check objects will inherit.
# It can be used, for instance, to test whether some object is Check-like
# using isinstance(obj, BaseCheck).
class BaseCheck:
    __slots__ = ()

# Check should support all three operations.
feature_mixins = (
    bincombo.AndSupportMixin, bincombo.OrSupportMixin, bincombo.InvertSupportMixin
)

# This is the type that would normally be returned by combinable() decorator.
# Here it replaces the original Check, but you could also give it a different name.
class Check(*feature_mixins, Check, BaseCheck):
    __slots__ = ()

# Objects of this type represent (possibly negated) AND/OR combinations of
# Check objects.
class CheckCombo(*feature_mixins, bincombo.Combo, BaseCheck):
    __slots__ = ()

    # Create a proxy check() method that queries an individual combo member.
    # bincombo will call this method for all members, combining the returned
    # boolean values using and/or, depending on the combination type, and possibly
    # invert the final result.
    @bincombo.combine_members
    def check(self, member, value):
        return member.check(value)

# This is an AND combination.
class AllChecks(bincombo.AndComboMixin, CheckCombo):
    __slots__ = ()

# This is an OR combination.
class AnyCheck(bincombo.OrComboMixin, CheckCombo):
    __slots__ = ()

# Finally, bincombo has to be taught all the types just created.
# By attaching the Config object to BaseCheck as a class attribute, both Check
# and CheckCombo instances will have it available due to inheritance.
BaseCheck.BIN_CONFIG = bincombo.Config(
    BaseCheck, Check, CheckCombo, AllChecks, AnyCheck
)

Further customization of the combining abilities is possible, the documentations of combinable(), combine_members() and Config have more information.

Release files for bincombo 0.2.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for bincombo 0.2.0
File Size Uploaded
bincombo-0.2.0.tar.gz 8.0 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for bincombo 0.2.0
File Interpreter ABI Platform
bincombo-0.2.0-py3-none-any.whl Python 3 none any Details

Total release size: 17.9 kB

Release files / bincombo-0.2.0.tar.gz

Download URL bincombo-0.2.0.tar.gz
Size 8.0 kB
Tags Source
SHA-256 checksum
How to use checksums
74793858bb6a09f627a3ed5c62b8259d46db4d0a281b88074403e56531f27308
BLAKE2b-256 checksum
How to use checksums
fa15a8c835cac73d67489813b8a777e2d526e623dcd0e536e630797f44266689
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via poetry/1.6.1 CPython/3.9.2 Linux/5.10.0-26-amd64

Release files / bincombo-0.2.0-py3-none-any.whl

Download URL bincombo-0.2.0-py3-none-any.whl
Size 10.0 kB
Tags Python 3
SHA-256 checksum
How to use checksums
cba619f07e4e54a70de4906a0c8b0ca900e1f04402c9652b82cfbba1cdb1407d
BLAKE2b-256 checksum
How to use checksums
d0d22fe6cb9b29358a4d9f5979b318c4341b3a26bbc53a0605d06f1869aa3f7a
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via poetry/1.6.1 CPython/3.9.2 Linux/5.10.0-26-amd64

Release history Release notifications | RSS feed

This release

0.2.0 This release

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page