Skip to main content

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

Project description

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.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

bincombo-0.2.0.tar.gz (8.0 kB view hashes)

Uploaded Source

Built Distribution

bincombo-0.2.0-py3-none-any.whl (10.0 kB view hashes)

Uploaded Python 3

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page