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.1.0.tar.gz (8.8 kB view details)

Uploaded Source

Built Distribution

bincombo-0.1.0-py3-none-any.whl (9.9 kB view details)

Uploaded Python 3

File details

Details for the file bincombo-0.1.0.tar.gz.

File metadata

  • Download URL: bincombo-0.1.0.tar.gz
  • Upload date:
  • Size: 8.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: poetry/1.1.14 CPython/3.7.3 Linux/5.10.0-0.bpo.15-amd64

File hashes

Hashes for bincombo-0.1.0.tar.gz
Algorithm Hash digest
SHA256 7a870afd86f9d646c70117c60f064530498af7b264b7983539623865678a6cd0
MD5 c6d3298165d999665fb3ad3701fb8f44
BLAKE2b-256 6cb47d4764f611eaeeafc31c2ec59301c6b60916b9b5b32af047a762de70e543

See more details on using hashes here.

File details

Details for the file bincombo-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: bincombo-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 9.9 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: poetry/1.1.14 CPython/3.7.3 Linux/5.10.0-0.bpo.15-amd64

File hashes

Hashes for bincombo-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 4cce5f67ef512f81f57fbe2c8315167793548460f5e9a74dd3a6c23e1e951fb9
MD5 49221b506b0a1cc1903c50c4ae35ea32
BLAKE2b-256 e321c366c441094c37f2400df61b040f6e8f004651e53a6d9094d36c0e76198c

See more details on using hashes here.

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