Skip to content

Commit da661b9

Browse files
committed
-
1 parent 969bbd5 commit da661b9

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

44 files changed

+3092
-435
lines changed

source_py2/python_toolbox/address_tools/string_to_object.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ def get_object_by_address(address, root=None, namespace={}):
105105
root = get_object_by_address(root)
106106
root_short_name = root.__name__.rsplit('.', 1)[-1]
107107

108-
if namespace:
108+
if namespace not in (None, {}):
109109
# And then for `namespace`:
110110
if isinstance(namespace, basestring):
111111
namespace = get_object_by_address(namespace)
@@ -136,7 +136,7 @@ def get_object_by_address(address, root=None, namespace={}):
136136
if root and (address == root_short_name):
137137
return root
138138

139-
if parent_object:
139+
if parent_object is not None:
140140

141141
if isinstance(parent_object, types.ModuleType) and \
142142
hasattr(parent_object, '__path__'):
@@ -166,7 +166,7 @@ def get_object_by_address(address, root=None, namespace={}):
166166
# `parent_object`. We try this before `namespace_dict` because
167167
# `parent_object` may have `__getattr__` or similar magic and our
168168
# object might be found through that:
169-
if parent_object and hasattr(parent_object, address):
169+
if (parent_object is not None) and hasattr(parent_object, address):
170170
return getattr(parent_object, address)
171171

172172
# Next is the `namespace_dict`:
File renamed without changes.

source_py2/python_toolbox/binary_search/binary_search_profile.py

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,12 @@
77
See its documentation for more info.
88
'''
99

10+
from python_toolbox import misc_tools
1011

1112
from .roundings import (Rounding, roundings, LOW, LOW_IF_BOTH,
1213
LOW_OTHERWISE_HIGH, HIGH, HIGH_IF_BOTH,
1314
HIGH_OTHERWISE_LOW, EXACT, CLOSEST, CLOSEST_IF_BOTH,
1415
BOTH)
15-
1616
from .functions import (binary_search, binary_search_by_index,
1717
make_both_data_into_preferred_rounding,
1818
_binary_search_both)
@@ -27,7 +27,8 @@ class BinarySearchProfile(object):
2727
than one time.
2828
'''
2929

30-
def __init__(self, sequence, function, value, both=None):
30+
def __init__(self, sequence, value, function=misc_tools.identity_function,
31+
both=None):
3132
'''
3233
Construct a `BinarySearchProfile`.
3334
@@ -42,10 +43,7 @@ def __init__(self, sequence, function, value, both=None):
4243
'''
4344

4445
if both is None:
45-
both = _binary_search_both(sequence, function, value)
46-
47-
if function is None:
48-
function = lambda x: x
46+
both = _binary_search_both(sequence, value, function=function)
4947

5048
self.results = {}
5149
'''
@@ -54,9 +52,9 @@ def __init__(self, sequence, function, value, both=None):
5452
'''
5553

5654
for rounding in roundings:
57-
self.results[rounding] = \
58-
make_both_data_into_preferred_rounding(both, function, value,
59-
rounding)
55+
self.results[rounding] = make_both_data_into_preferred_rounding(
56+
both, value, function=function, rounding=rounding
57+
)
6058
none_count = list(both).count(None)
6159

6260
self.all_empty = (none_count == 2)

source_py2/python_toolbox/binary_search/functions.py

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,16 @@
1414
# todo: ensure there are no `if variable` checks where we're thinking of None
1515
# but the variable might be False
1616

17+
from python_toolbox import misc_tools
18+
1719
from .roundings import (Rounding, roundings, LOW, LOW_IF_BOTH,
1820
LOW_OTHERWISE_HIGH, HIGH, HIGH_IF_BOTH,
1921
HIGH_OTHERWISE_LOW, EXACT, CLOSEST, CLOSEST_IF_BOTH,
2022
BOTH)
2123

22-
def binary_search_by_index(sequence, function, value, rounding=CLOSEST):
24+
def binary_search_by_index(sequence, value,
25+
function=misc_tools.identity_function,
26+
rounding=CLOSEST):
2327
'''
2428
Do a binary search, returning answer as index number.
2529
@@ -29,15 +33,15 @@ def binary_search_by_index(sequence, function, value, rounding=CLOSEST):
2933
3034
For documentation of rounding options, check `binary_search.roundings`.
3135
'''
32-
if function is None:
33-
function = lambda x: x
3436
my_range = xrange(len(sequence))
3537
fixed_function = lambda index: function(sequence[index])
36-
result = binary_search(my_range, fixed_function, value, rounding)
38+
result = binary_search(my_range, value, function=fixed_function,
39+
rounding=rounding)
3740
return result
3841

3942

40-
def _binary_search_both(sequence, function, value):
43+
def _binary_search_both(sequence, value,
44+
function=misc_tools.identity_function):
4145
'''
4246
Do a binary search through a sequence with the `BOTH` rounding.
4347
@@ -52,9 +56,6 @@ def _binary_search_both(sequence, function, value):
5256

5357
### Preparing: ############################################################
5458
# #
55-
if function is None:
56-
function = lambda x: x
57-
5859
get = lambda number: function(sequence[number])
5960

6061
low = 0
@@ -100,7 +101,8 @@ def _binary_search_both(sequence, function, value):
100101

101102

102103

103-
def binary_search(sequence, function, value, rounding=CLOSEST):
104+
def binary_search(sequence, value, function=misc_tools.identity_function,
105+
rounding=CLOSEST):
104106
'''
105107
Do a binary search through a sequence.
106108
@@ -120,11 +122,13 @@ def binary_search(sequence, function, value, rounding=CLOSEST):
120122

121123
from .binary_search_profile import BinarySearchProfile
122124

123-
binary_search_profile = BinarySearchProfile(sequence, function, value)
125+
binary_search_profile = BinarySearchProfile(sequence, value,
126+
function=function)
124127
return binary_search_profile.results[rounding]
125128

126129

127-
def make_both_data_into_preferred_rounding(both, function, value, rounding):
130+
def make_both_data_into_preferred_rounding(
131+
both, value, function=misc_tools.identity_function, rounding=BOTH):
128132
'''
129133
Convert results gotten using `BOTH` to a different rounding option.
130134

source_py2/python_toolbox/caching/cached_property.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,3 +74,8 @@ def inner(same_method_function, self_obj, *args, **kwargs):
7474
with getattr(self_obj, self.get_our_name(self_obj)):
7575
return method_function(self_obj, *args, **kwargs)
7676
return decorator_tools.decorator(inner, method_function)
77+
78+
79+
def __repr__(self):
80+
return '<%s: %s>' % (type(self).__name__, self.our_name or self.getter)
81+

source_py2/python_toolbox/caching/decorators.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -101,9 +101,9 @@ def decorator(function):
101101
def remove_expired_entries():
102102
almost_cutting_point = \
103103
binary_search.binary_search_by_index(
104-
cached._cache.keys(),
105-
sorting_key_function,
104+
list(cached._cache.keys()),
106105
_get_now(),
106+
sorting_key_function,
107107
rounding=binary_search.LOW
108108
)
109109
if almost_cutting_point is not None:
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import math
2+
from python_toolbox import cute_iter_tools
3+
from python_toolbox import cute_testing
4+
from python_toolbox.math_tools import binomial # Making it easy to find
5+
6+
from .chain_space import ChainSpace
7+
from .product_space import ProductSpace
8+
from .map_space import MapSpace
9+
from .selection_space import SelectionSpace
10+
from .perm_space import PermSpace
11+
from .comb_space import CombSpace
12+
from .perm import Perm
13+
from .comb import Comb
14+
15+
Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
import collections
2+
import types
3+
import sys
4+
import math
5+
import numbers
6+
7+
from python_toolbox import misc_tools
8+
from python_toolbox import binary_search
9+
from python_toolbox import dict_tools
10+
from python_toolbox import nifty_collections
11+
from python_toolbox import caching
12+
13+
from python_toolbox import math_tools
14+
from python_toolbox import sequence_tools
15+
from python_toolbox import cute_iter_tools
16+
from python_toolbox import nifty_collections
17+
18+
from . import misc
19+
from python_toolbox import misc_tools
20+
21+
infinity = float('inf')
22+
23+
24+
25+
class ChainSpace(sequence_tools.CuteSequenceMixin, collections.Sequence):
26+
'''
27+
A space of sequences chained together.
28+
29+
This is similar to `itertools.chain`, except that items can be fetched by
30+
index number rather than just iteration.
31+
'''
32+
def __init__(self, sequences):
33+
34+
self.sequences = nifty_collections.LazyTuple(
35+
(sequence_tools.ensure_iterable_is_immutable_sequence(
36+
sequence, default_type=nifty_collections.LazyTuple)
37+
for sequence in sequences)
38+
)
39+
@caching.CachedProperty
40+
@nifty_collections.LazyTuple.factory()
41+
def accumulated_lengths(self):
42+
'''
43+
A sequence of the accumulated length as every sequence is added.
44+
45+
For example, if this chain space has sequences with lengths of 10, 100
46+
and 1000, this would be `[0, 10, 110, 1110]`.
47+
'''
48+
total = 0
49+
yield 0
50+
for sequence in self.sequences:
51+
total += sequence_tools.get_length(sequence)
52+
yield total
53+
54+
55+
length = caching.CachedProperty(lambda self: self.accumulated_lengths[-1])
56+
57+
58+
def __repr__(self):
59+
return '<%s: %s>' % (
60+
type(self).__name__,
61+
'+'.join(str(len(sequence)) for sequence in self.sequences),
62+
)
63+
64+
def __getitem__(self, i):
65+
if isinstance(i, slice):
66+
raise NotImplementedError
67+
assert isinstance(i, int)
68+
if i <= -1:
69+
i += self.length
70+
if i < 0:
71+
raise IndexError
72+
if self.accumulated_lengths.is_exhausted and i >= self.length:
73+
raise IndexError
74+
# Todo: Can't have a binary search here, it exhausts all the sequences.
75+
sequence_index = binary_search.binary_search_by_index(
76+
self.accumulated_lengths, i, rounding=binary_search.LOW_IF_BOTH
77+
)
78+
if sequence_index is None:
79+
raise IndexError
80+
sequence_start = self.accumulated_lengths[sequence_index]
81+
return self.sequences[sequence_index][i - sequence_start]
82+
83+
84+
def __iter__(self):
85+
for sequence in self.sequences:
86+
yield from sequence
87+
88+
_reduced = property(lambda self: (type(self), self.sequences))
89+
90+
__eq__ = lambda self, other: (isinstance(other, ChainSpace) and
91+
self._reduced == other._reduced)
92+
93+
def __contains__(self, item):
94+
return any(item in sequence for sequence in self.sequences
95+
if (not isinstance(sequence, str) or isinstance(item, str)))
96+
97+
def index(self, item):
98+
for sequence, accumulated_length in zip(self.sequences,
99+
self.accumulated_lengths):
100+
try:
101+
index_in_sequence = sequence.index(item)
102+
except ValueError:
103+
pass
104+
except TypeError:
105+
assert isinstance(sequence, (str, bytes)) and \
106+
(not isinstance(item, (str, bytes)))
107+
else:
108+
return index_in_sequence + accumulated_length
109+
else:
110+
raise ValueError
111+
112+
def __bool__(self):
113+
try: next(iter(self))
114+
except StopIteration: return False
115+
else: return True
116+
117+
118+
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
from python_toolbox import math_tools
2+
from python_toolbox import caching
3+
4+
from .perm import Perm
5+
from .comb_space import CombSpace
6+
7+
8+
class Comb(Perm):
9+
def __init__(self, number_or_perm_sequence, perm_space):
10+
assert isinstance(perm_space, CombSpace)
11+
Perm.__init__(self, number_or_perm_sequence=number_or_perm_sequence,
12+
perm_space=perm_space)
13+
14+
@caching.CachedProperty
15+
def _perm_sequence(self):
16+
assert (0 <= self.number <
17+
self.just_dapplied_rapplied_perm_space.length)
18+
wip_number = (self.just_dapplied_rapplied_perm_space.length - 1 -
19+
self.number)
20+
wip_perm_sequence = []
21+
for i in range(self.just_dapplied_rapplied_perm_space.n_elements,
22+
0, -1):
23+
for j in range(self.just_dapplied_rapplied_perm_space.
24+
sequence_length, i - 2, -1):
25+
candidate = math_tools.binomial(j, i)
26+
if candidate <= wip_number:
27+
wip_perm_sequence.append(
28+
self.just_dapplied_rapplied_perm_space.sequence[-(j+1)]
29+
)
30+
wip_number -= candidate
31+
break
32+
else:
33+
raise RuntimeError
34+
result = tuple(wip_perm_sequence)
35+
assert len(result) == self.length
36+
return result
37+
38+
39+
@caching.CachedProperty
40+
def number(self):
41+
'''
42+
43+
The number here is not necessarily the number with which the perm was
44+
fetched from the perm space; it's the number of the perm in a perm
45+
space that is neither degreed, fixed or sliced.
46+
'''
47+
if self.is_rapplied or self.is_dapplied:
48+
return self.unrapplied.undapplied.number
49+
50+
processed_perm_sequence = tuple(
51+
self.just_dapplied_rapplied_perm_space.sequence_length - 1 -
52+
item for item in self._perm_sequence[::-1]
53+
)
54+
return self.just_dapplied_rapplied_perm_space.length - 1 - sum(
55+
(math_tools.binomial(item, i) for i, item in
56+
enumerate(processed_perm_sequence, start=1)),
57+
0
58+
)
59+

0 commit comments

Comments
 (0)