Contact
CoCalc Logo Icon
StoreFeaturesDocsShareSupport News AboutSign UpSign In
| Download
Views: 19204
1
r"""
2
Improved container classes
3
==========================
4
5
The ``containers`` module defines improved container classes, such as lists:
6
7
* `List`: a subclass of the builtin ``list`` class, with added methods, such as ``index_append``;
8
* `Bijectivelist`: a replacement for the ``list`` class for use with 1-1 relationships
9
where index lookup via ``dict`` makes sense; and
10
* `ShelveBijectivelist`: a replacement for the ``list`` class for use with 1-1 relationships
11
where index lookup via ``shelve`` makes sense.
12
This class uses ``shelve`` to cope with cases where a ``dict`` would be too large to store in memory.
13
14
AUTHORS:
15
16
- Paul Leopardi (2016-08-21): initial version
17
18
"""
19
#*****************************************************************************
20
# Copyright (C) 2016-2017 Paul Leopardi [email protected]
21
#
22
# Distributed under the terms of the GNU General Public License (GPL)
23
# as published by the Free Software Foundation; either version 2 of
24
# the License, or (at your option) any later version.
25
# http://www.gnu.org/licenses/
26
#*****************************************************************************
27
28
import glob
29
import os
30
import shelve
31
32
from sage.misc.temporary_file import tmp_filename
33
from sage.structure.sage_object import SageObject
34
35
from boolean_cayley_graphs.saveable import Saveable
36
37
38
class List(list, SageObject, Saveable):
39
r"""
40
Subclass of ``list`` with added methods, such as ``index_append``.
41
42
TESTS:
43
44
::
45
46
sage: from boolean_cayley_graphs.containers import List
47
sage: L = List([1,2,4])
48
sage: print(L)
49
[1, 2, 4]
50
51
sage: from boolean_cayley_graphs.containers import List
52
sage: L = List([1,2,4])
53
sage: latex(L)
54
\text{\texttt{[1,{ }2,{ }4]}}
55
"""
56
57
58
def index_append(self, item):
59
r"""
60
Return the index of a given item, appending it if necessary.
61
62
If the inherited list ``index`` method for ``self`` yields a ``ValueError`,
63
then set result to the length of `self``, and append item to ``self``.
64
65
INPUT:
66
67
- ``self`` -- the current object.
68
- ``item`` -- the item to look up, and append if necessary.
69
70
OUTPUT:
71
72
A non-negative integer indicating the index of ``item`` within ``self``.
73
74
EFFECT:
75
76
The item ``item`` may be appended to ``self``.
77
78
EXAMPLES:
79
80
::
81
82
sage: from boolean_cayley_graphs.containers import List
83
sage: L = List([1,2,4])
84
sage: L.index_append(2)
85
1
86
sage: L
87
[1, 2, 4]
88
sage: L.index_append(3)
89
3
90
sage: L
91
[1, 2, 4, 3]
92
sage: del L
93
"""
94
try:
95
result = self.index(item)
96
except ValueError:
97
result = len(self)
98
self.append(item)
99
return result
100
101
102
class BijectiveList(SageObject, Saveable):
103
r"""
104
Replacement for the ``list`` class with only a few methods,
105
such as ``__getitem__``, ``index``, and ``index_append``.
106
107
List lookup for ``__getitem__`` uses a list named ``_item``.
108
Index lookup for ``index`` and ``index_append`` uses a dict named ``_index`.
109
This class is used for 1-1 relationships where index lookup via ``dict`` makes sense.
110
111
.. WARNING::
112
113
Initialization from a non-empty list can easily break
114
the 1-1 relationship between index and item in a ``BijectiveList``.
115
116
EXAMPLES:
117
118
Initialize from a list.
119
120
::
121
122
sage: from boolean_cayley_graphs.containers import BijectiveList
123
sage: BL = BijectiveList(["1","2","3"])
124
sage: BL.get_list()
125
['1', '2', '3']
126
sage: dict(sorted(BL.get_dict().items()))
127
{'1': 0, '2': 1, '3': 2}
128
sage: del BL
129
130
TESTS:
131
132
::
133
134
sage: from boolean_cayley_graphs.containers import BijectiveList
135
sage: L = BijectiveList([1,2,4])
136
sage: print(L)
137
BijectiveList(1,2,4)
138
139
sage: from boolean_cayley_graphs.containers import BijectiveList
140
sage: L = BijectiveList([1,2,4])
141
sage: latex(L)
142
\text{\texttt{BijectiveList(1,2,4)}}
143
"""
144
def __init__(self, other_list=None):
145
r"""
146
Constructor.
147
148
EXAMPLES:
149
150
Default initialization.
151
152
::
153
154
sage: from boolean_cayley_graphs.containers import BijectiveList
155
sage: BL = BijectiveList()
156
sage: BL.get_list()
157
[]
158
sage: BL.get_dict()
159
{}
160
sage: del BL
161
162
TESTS:
163
164
Initialize from a list.
165
166
::
167
168
sage: from boolean_cayley_graphs.containers import BijectiveList
169
sage: BL = BijectiveList(["1","2","6"])
170
sage: BL.get_list()
171
['1', '2', '6']
172
sage: dict(sorted(BL.get_dict().items()))
173
{'1': 0, '2': 1, '6': 2}
174
sage: del BL
175
"""
176
if other_list == None:
177
self._item = []
178
self._index = {}
179
else:
180
self._item = other_list
181
self._index = dict((other_list[index],index)
182
for index in range(len(other_list)))
183
184
185
def _repr_(self):
186
r"""
187
Sage string representation.
188
189
INPUT:
190
191
- ``self`` -- the current object.
192
193
EXAMPLES:
194
195
::
196
197
sage: from boolean_cayley_graphs.containers import BijectiveList
198
sage: L = BijectiveList([1,2,4])
199
sage: print(L)
200
BijectiveList(1,2,4)
201
"""
202
return (
203
type(self).__name__ +
204
"(" +
205
",".join([repr(item) for item in self._item]) +
206
")")
207
208
209
def __getitem__(self, index):
210
r"""
211
List lookup by index.
212
213
INPUT:
214
215
- ``self`` -- the current object.
216
- ``index`` -- the index to look up.
217
218
EXAMPLES:
219
220
::
221
222
sage: from boolean_cayley_graphs.containers import BijectiveList
223
sage: BL = BijectiveList([1,2,3])
224
sage: BL[2]
225
3
226
sage: del BL
227
"""
228
return self._item[index]
229
230
231
def __len__(self):
232
r"""
233
Get the length of the list.
234
235
INPUT:
236
237
- ``self`` -- the current object.
238
239
EXAMPLES:
240
241
::
242
243
sage: from boolean_cayley_graphs.containers import BijectiveList
244
sage: BL = BijectiveList([1,2,3])
245
sage: len(BL)
246
3
247
sage: del BL
248
"""
249
return len(self._item)
250
251
252
def get_dict(self):
253
r"""
254
Get the ``dict`` part of the ``BijectiveList``.
255
256
INPUT:
257
258
- ``self`` -- the current object.
259
260
EXAMPLES:
261
262
::
263
264
sage: from boolean_cayley_graphs.containers import BijectiveList
265
sage: BL = BijectiveList([1,2,5])
266
sage: dict(sorted(BL.get_dict().items()))
267
{1: 0, 2: 1, 5: 2}
268
sage: del BL
269
"""
270
return self._index
271
272
273
def get_list(self):
274
r"""
275
Get the ``list`` part of the ``BijectiveList``.
276
277
INPUT:
278
279
- ``self`` -- the current object.
280
281
EXAMPLES:
282
283
::
284
285
sage: from boolean_cayley_graphs.containers import BijectiveList
286
sage: BL = BijectiveList([1,2,5])
287
sage: BL.get_list()
288
[1, 2, 5]
289
sage: del BL
290
291
"""
292
return self._item
293
294
295
def index(self,item):
296
r"""
297
Return the index of a given item.
298
299
Use a ``dict`` lookup using ``_index`` instead of calling ``index`` on the list.
300
If the ``dict`` lookup yields a ``KeyError`` then raise a ``ValueError``.
301
302
INPUT:
303
304
- ``self`` -- the current object.
305
- ``item`` -- the item to look up.
306
307
OUTPUT:
308
309
A non-negative integer indicating the index of ``item`` within ``self``.
310
311
EXAMPLES:
312
313
::
314
315
sage: from boolean_cayley_graphs.containers import BijectiveList
316
sage: BL = BijectiveList([1,2,4])
317
sage: BL.index(2)
318
1
319
sage: BL.get_list()
320
[1, 2, 4]
321
sage: dict(sorted(BL.get_dict().items()))
322
{1: 0, 2: 1, 4: 2}
323
sage: del BL
324
325
TESTS:
326
327
::
328
329
sage: from boolean_cayley_graphs.containers import BijectiveList
330
sage: BL = BijectiveList([1,2,4])
331
sage: try:
332
....: BL.index(3)
333
....: except ValueError as e:
334
....: print("ValueError: {0}".format(e.args[0]))
335
....: finally:
336
....: del BL
337
ValueError: 3 is not in list
338
"""
339
try:
340
result = self._index[item]
341
except KeyError:
342
raise ValueError("{} is not in list".format(item))
343
return result
344
345
346
def index_append(self, item):
347
r"""
348
Return the index of a given item, appending it if necessary.
349
350
Use a ``dict`` lookup using ``_index`` instead of calling ``index`` on the list.
351
If the dict lookup yields a `KeyError`` then set result to the length of ``self``,
352
append item to ``self``, and add result to ``_index``.
353
354
INPUT:
355
356
- ``self`` -- the current object.
357
- ``item`` -- the item to look up, and append if necessary.
358
359
OUTPUT:
360
361
A non-negative integer indicating the index of ``item`` within ``self``.
362
363
EFFECT:
364
365
The item ``item`` may be appended to ``self``.
366
367
EXAMPLES:
368
369
::
370
371
sage: from boolean_cayley_graphs.containers import BijectiveList
372
sage: BL = BijectiveList([1,2,4])
373
sage: BL.index_append(2)
374
1
375
sage: BL.get_list()
376
[1, 2, 4]
377
sage: BL.index_append(3)
378
3
379
sage: BL.get_list()
380
[1, 2, 4, 3]
381
sage: dict(sorted(BL.get_dict().items()))
382
{1: 0, 2: 1, 3: 3, 4: 2}
383
sage: del BL
384
"""
385
try:
386
result = self._index[item]
387
except KeyError:
388
result = len(self._item)
389
self._item.append(item)
390
self._index[item] = result
391
return result
392
393
394
def sync(self):
395
r"""
396
Dummy method to match the interface of ``ShelveBijectiveList``.
397
398
TESTS:
399
400
::
401
402
sage: from boolean_cayley_graphs.containers import BijectiveList
403
sage: BL = BijectiveList(["1","2","6"])
404
sage: BL.sync()
405
sage: del BL
406
"""
407
pass
408
409
410
def close_dict(self):
411
r"""
412
Dummy method to match the interface of ``ShelveBijectiveList``.
413
414
TESTS:
415
416
::
417
418
sage: from boolean_cayley_graphs.containers import BijectiveList
419
sage: BL = BijectiveList(["1","2","6"])
420
sage: BL.close_dict()
421
sage: BL.remove_dict()
422
"""
423
pass
424
425
426
def remove_dict(self):
427
r"""
428
Remove the dictionary.
429
430
TESTS:
431
432
::
433
434
sage: from boolean_cayley_graphs.containers import BijectiveList
435
sage: BL = BijectiveList(["1","2","6"])
436
sage: BL.close_dict()
437
sage: BL.remove_dict()
438
sage: try:
439
....: BL._index
440
....: except AttributeError:
441
....: pass
442
"""
443
try:
444
del self._index
445
except AttributeError:
446
pass
447
448
449
def __del__(self):
450
r"""
451
Clean up by closing and removing the dictionary,
452
before deleting the current object.
453
454
TESTS:
455
456
::
457
458
sage: from boolean_cayley_graphs.containers import BijectiveList
459
sage: BL = BijectiveList(["1","2","6"])
460
sage: del BL
461
sage: try:
462
....: BL
463
....: except NameError:
464
....: pass
465
"""
466
self.close_dict()
467
self.remove_dict()
468
469
470
class ShelveBijectiveList(BijectiveList):
471
r"""
472
Replacement for the ``list`` class with only a few methods,
473
such as ``__getitem__``, ``index``, and ``index_append``.
474
475
List lookup for ``__getitem__`` uses a list named ``_item``.
476
Index lookup for ``index`` and ``index_append`` uses a ``shelve`` named ``_index``.
477
This class is used for 1-1 relationships where index lookup via ``shelve`` makes sense.
478
479
.. NOTE::
480
481
This class uses ``shelve`` to cope with situations
482
where a ``dict`` would be too large to fit into memory.
483
484
.. WARNING::
485
486
Initialization from a non-empty list works only for lists of strings.
487
488
.. WARNING::
489
490
Initialization from a non-empty list can easily break
491
the 1-1 relationship between index and item in a ``ShelveBijectiveList``.
492
493
EXAMPLES:
494
495
Initialize from a list.
496
497
::
498
499
sage: from boolean_cayley_graphs.containers import ShelveBijectiveList
500
sage: SBL = ShelveBijectiveList(["1","2","4"])
501
sage: SBL.get_list()
502
['1', '2', '4']
503
sage: dict(sorted(SBL.get_dict().items()))
504
{'1': 0, '2': 1, '4': 2}
505
sage: del SBL
506
507
TESTS:
508
509
::
510
511
sage: from boolean_cayley_graphs.containers import ShelveBijectiveList
512
sage: L = ShelveBijectiveList(["1","2","4"])
513
sage: print(L)
514
ShelveBijectiveList('1','2','4')
515
516
sage: from boolean_cayley_graphs.containers import ShelveBijectiveList
517
sage: L = ShelveBijectiveList(["1","2","4"])
518
sage: latex(L)
519
\text{\texttt{ShelveBijectiveList('1','2','4')}}
520
"""
521
def __init__(self, other_list=None):
522
r"""
523
Constructor.
524
525
EXAMPLES:
526
527
Default initialization.
528
529
::
530
531
sage: from boolean_cayley_graphs.containers import ShelveBijectiveList
532
sage: SBL = ShelveBijectiveList()
533
sage: SBL.get_list()
534
[]
535
sage: dict(SBL.get_dict())
536
{}
537
sage: del SBL
538
539
TESTS:
540
541
Initialize from a list.
542
543
::
544
545
sage: from boolean_cayley_graphs.containers import ShelveBijectiveList
546
sage: SBL = ShelveBijectiveList(["1","2","6"])
547
sage: SBL.get_list()
548
['1', '2', '6']
549
sage: dict(sorted(SBL.get_dict().items()))
550
{'1': 0, '2': 1, '6': 2}
551
sage: del SBL
552
"""
553
self.shelve_file_name = tmp_filename(ext=".index")
554
# Work around http://bugs.python.org/issue18039 not fixed in 2.7*
555
self.remove_dict()
556
self._index = shelve.open(self.shelve_file_name, flag='n')
557
if other_list == None:
558
self._item = []
559
else:
560
self._item = other_list
561
for index in range(len(other_list)):
562
item = other_list[index]
563
self._index[item] = index
564
565
566
def sync(self):
567
r"""
568
Synchronize the persistent dictionary on disk, if feasible.
569
570
TESTS:
571
572
::
573
574
sage: from boolean_cayley_graphs.containers import ShelveBijectiveList
575
sage: SBL = ShelveBijectiveList(["1","2","6"])
576
sage: SBL.sync()
577
sage: del SBL
578
"""
579
self._index.sync()
580
581
582
def close_dict(self):
583
r"""
584
Synchronize and close the persistent dictionary on disk.
585
586
TESTS:
587
588
::
589
590
sage: from boolean_cayley_graphs.containers import ShelveBijectiveList
591
sage: SBL = ShelveBijectiveList(["1","2","6"])
592
sage: SBL.close_dict()
593
sage: SBL.remove_dict()
594
"""
595
self._index.close()
596
597
598
def remove_dict(self):
599
r"""
600
Remove the files used for the persistent dictionary on disk.
601
602
.. WARNING::
603
604
Use ``close_dict`` first.
605
606
TESTS:
607
608
::
609
610
sage: import glob
611
sage: from boolean_cayley_graphs.containers import ShelveBijectiveList
612
sage: SBL = ShelveBijectiveList(["1","2","6"])
613
sage: SBL.close_dict()
614
sage: SBL.remove_dict()
615
sage: glob.glob(SBL.shelve_file_name + "*")
616
[]
617
"""
618
for file_name in glob.glob(self.shelve_file_name + "*"):
619
if os.path.isfile(file_name):
620
os.remove(file_name)
621
622
623
def __del__(self):
624
r"""
625
Clean up by closing the persistent dictionary on disk, and
626
removing the files used for it, before deleting the current object.
627
628
TESTS:
629
630
::
631
632
sage: import glob
633
sage: from boolean_cayley_graphs.containers import ShelveBijectiveList
634
sage: SBL = ShelveBijectiveList(["1","2","6"])
635
sage: shelve_file_name = SBL.shelve_file_name
636
sage: del SBL
637
sage: glob.glob(shelve_file_name + "*")
638
[]
639
sage: try:
640
....: SBL
641
....: except NameError:
642
....: pass
643
"""
644
self.close_dict()
645
self.remove_dict()
646
647
648