Ignore changes in the amount of whitespace
Differences between version dated 2010-08-10 20:09:26 and 2011-08-31 21:11:41
(spanning 2 versions)
Deletions are marked like this.
Additions are marked like this.
Test(IsInstance(Local('x')), Class(Y))
``Conjunction`` subclasses, on the other hand, are used to "and" together
``Conjunction`` instances, on the other hand, are used to "and" together
criteria that apply to the same dispatch expression. For example, this
expression::
would be represented internally like this::
Test(IsInstance(Local('x')), Classes([Class(Y), Class(Z)]))
(That is, ``Classes`` is a ``Conjunction`` subclass representing the "and" of
multiple ``Class`` criteria.)
Test(IsInstance(Local('x')), Conjunction([Class(Y), Class(Z)]))
The rest of this document describes how predicates, signatures, tests, dispatch
expressions, and criteria work together to create expressions in disjunctive
``False`` from the resulting predicate, and conditions that can never be true
are not used for indexing or dispatching.
Another special case is tuples containing nested tuples::
>>> disjuncts( (float, (int, str)) )
[(<type 'float'>, <type 'int'>),
(<type 'float'>, <type 'str'>)]
>>> disjuncts( ((int, str), object) )
[(<type 'int'>, <type 'object'>),
(<type 'str'>, <type 'object'>)]
>>> disjuncts( (object, (int, str), float) )
[(<type 'object'>, <type 'int'>, <type 'float'>),
(<type 'object'>, <type 'str'>, <type 'float'>)]
>>> disjuncts( ((int, str), (int, str)) )
[(<type 'int'>, <type 'int'>),
(<type 'str'>, <type 'int'>),
(<type 'int'>, <type 'str'>),
(<type 'str'>, <type 'str'>)]
This lets you avoid writing lots of decorators for the cases where you want
more than one type (or ``istype()`` instance) to match in a given argument
position. (As you can see, it's equivalent to specifying all the individual
combinations of specified types.)
Finally, the ``negate()`` function inverts the truth of a condition, e.g.::
>>> negate(True)
``y is x`` condition would be true. Conversely, ``IsObject(x, False)``
represents the set of objects ``y`` for whom ``y is not x``::
>>> from peak.rules.criteria import IsObject, NotObjects
>>> from peak.rules.criteria import IsObject, Conjunction
>>> o = object()
>>> is_o = IsObject(o)
False
And the intersection of multiple ``is not`` conditions produces a
``NotObjects`` set::
``Conjunction``::
>>> not_foo = IsObject("foo", False)
>>> not_bar = IsObject("bar", False)
>>> not_foobar = intersect(not_foo, not_bar)
>>> not_foobar
NotObjects([IsObject('foo', False), IsObject('bar', False)])
Conjunction([IsObject('foo', False), IsObject('bar', False)])
Which of course then implies each of the individual "not" conditions::
>>> implies(not_foobar, IsObject("bar"))
False
Oh, and an ``is`` condition implies any ``NotObjects`` that don't contain its
Oh, and an ``is`` condition implies any ``Conjunction`` that don't contain its
opposite::
>>> implies(is_o, not_foobar)
>>> implies(not_foobar, is_o)
False
Finally, negating a ``NotObjects`` returns a disjunction of true ``IsObject``
tests, and vice versa::
Finally, negating a ``Conjunction`` of is-nots returns a disjunction of true
``IsObject`` tests, and vice versa::
>>> negate(not_foobar)
DisjunctionSet([IsObject('foo', True), IsObject('bar', True)])
>>> negate(DisjunctionSet([IsObject('foo'), IsObject('bar')]))
NotObjects([IsObject('foo', False), IsObject('bar', False)])
Conjunction([IsObject('foo', False), IsObject('bar', False)])
Values and Ranges
>>> intersect(Class(object), Class(int))
Class(<type 'int'>, True)
The intersection of two or more unrelated ``Class`` criteria is a ``Classes``
set::
The intersection of two or more unrelated ``Class`` criteria is represented by
a ``Conjunction``::
>>> from peak.rules.criteria import Classes
>>> from peak.rules.criteria import Conjunction
>>> intersect(Class(int, False), Class(str, False)) == Classes(
>>> intersect(Class(int, False), Class(str, False)) == Conjunction(
... [Class(int, False), Class(str, False)]
... )
True
``Classes`` is a subclass of ``Conjunction``, so all the standard
rules of intersection and implication apply.
Exact-Type and Type-Exclusion Tests
===================================
False
Unless both are exclusion tests on different types, in which case their
intersection is a ``Classes()`` conjunction::
intersection is a ``Conjunction`` of the two::
>>> intersect(istype(str, False), istype(int, False)) == Classes([
>>> intersect(istype(str, False), istype(int, False)) == Conjunction([
... istype(int, False), istype(str, False)
... ])
True
istype(<type 'object'>, True)
But when it's intersected with a type exclusion test, the result is a
``Classes`` conjunction::
``Conjunction``::
>>> intersect(istype(int, False), Class(str)) == Classes([
>>> intersect(istype(int, False), Class(str)) == Conjunction([
... istype(int, False), Class(str, True)
... ])
True
>>> s = intersect(Class(str), istype(int, False))
>>> s == Classes([istype(int, False), Class(str, True)])
>>> s == Conjunction([istype(int, False), Class(str, True)])
True
>>> intersect(s, istype(int))
>>> negate(
... Test('x',
... NotObjects([IsObject('foo',False), IsObject('bar',False)])
... Conjunction([IsObject('foo',False), IsObject('bar',False)])
... )
... ) == DisjunctionSet(
... [Test('x', IsObject('foo', True)),
criterion is the intersection of the original tests' criteria::
>>> intersect(x_isa_int, Test("x", Class(str))) == Test(
... 'x', Classes([Class(int), Class(str)])
... 'x', Conjunction([Class(int), Class(str)])
... )
True
>>> intersect(x_int_y_str, Test("y", Class(float))) == Signature([
... Test('x', Class(int)),
... Test('y', Classes([Class(str), Class(float)]))
... Test('y', Conjunction([Class(str), Class(float)]))
... ])
True
>>> intersect(x_int_y_str, Test("x", Class(float))) == Signature([
... Test('x', Classes([Class(int), Class(float)])),
... Test('x', Conjunction([Class(int), Class(float)])),
... Test('y', Class(str))
... ])
True
>>> intersect(Test("x", Class(float)), x_int_y_str) == Signature([
... Test('x', Classes([Class(int), Class(float)])),
... Test('x', Conjunction([Class(int), Class(float)])),
... Test('y', Class(str))
... ])
True
>>> DisjunctionSet([OrElse([Class(a), Class(b)])]) == DisjunctionSet([
... Class(a, True),
... Classes([Class(a, False), Class(b, True)])
... Conjunction([Class(a, False), Class(b, True)])
... ])
True
>>> set(disjuncts(OrElse([istype(int), a_or_b]))) == set([
... istype(int),
... Classes([istype(int, False), Class(b)]),
... Classes([istype(int, False), Class(a)])
... Conjunction([istype(int, False), Class(b)]),
... Conjunction([istype(int, False), Class(a)])
... ])
True
We'll do one more test, to show that the disjuncts of the negated portions of
the ``OrElse`` are also expanded::
>>> a_and_b = Classes([Class(a), Class(b)])
>>> a_and_b = Conjunction([Class(a), Class(b)])
>>> not_a = Class(a, False)
>>> not_b = Class(b, False)
>>> int_or_str = DisjunctionSet([Class(int), Class(str)])
>>> set(disjuncts(OrElse([a_and_b, int_or_str]))) == set([
... a_and_b, Classes([not_a, Class(int)]), Classes([not_a, Class(str)]),
... Classes([not_b, Class(int)]), Classes([not_b, Class(str)])
... a_and_b, Conjunction([not_a, Class(int)]), Conjunction([not_a, Class(str)]),
... Conjunction([not_b, Class(int)]), Conjunction([not_b, Class(str)])
... ])
True