istr-python 1.1.22__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,949 @@
1
+ Metadata-Version: 2.4
2
+ Name: istr-python
3
+ Version: 1.1.22
4
+ Summary: istr - strings you can count on
5
+ Author-email: Ruud van der Ham <rt.van.der.ham@gmail.com>
6
+ Project-URL: Homepage, https://github.com/salabim/istr
7
+ Project-URL: Repository, https://github.com/salabim/istr
8
+ Classifier: Development Status :: 5 - Production/Stable
9
+ Classifier: License :: OSI Approved :: MIT License
10
+ Classifier: Programming Language :: Python :: 3 :: Only
11
+ Requires-Python: >=3.10
12
+ Description-Content-Type: text/markdown
13
+
14
+ <img src="https://www.salabim.org/istr/istr_logo.png" width=500>
15
+
16
+ ### Introduction
17
+
18
+ The `istr` module makes it possible to use strings as if they were integers.
19
+
20
+ ### Changelog
21
+
22
+ For the changelog, see www.salabim.org/istr/changelog .
23
+
24
+ ### Background
25
+
26
+ Using strings as if they were integers can be very handy for solving puzzles, but also for other purposes.
27
+ For instance, the famous send more money puzzle, where each letter has to be replaced by a unique digit (0-9)
28
+
29
+ ```
30
+ S E N D
31
+ M O R E
32
+ --------- +
33
+ M O N E Y
34
+ ```
35
+ can be nicely, albeit not very efficient, coded as:
36
+ ```
37
+ import istr
38
+
39
+ for s, e, n, d, m, o, r, y in istr.permutations(range(10), 8):
40
+ if m and ((s|e|n|d) + (m|o|r|e) == (m|o|n|e|y):
41
+ print(f' {s|e|n|d}')
42
+ print(f' {m|o|r|e}')
43
+ print('-----')
44
+ print(f'{m|o|n|e|y}')
45
+ ```
46
+
47
+ or even
48
+
49
+ ```
50
+ import istr
51
+
52
+ for S, E, N, D, M, O, R, Y in istr.permutations(range(10), 8):
53
+ if M and (istr("=SEND") + istr("=MORE")) == istr("=MONEY"):
54
+ print(" " | istr("=SEND"))
55
+ print(" " | istr("=MORE"))
56
+ print("-----")
57
+ print(istr("=MONEY"))
58
+ ```
59
+
60
+ Of, if we want to add all the digits in a string:
61
+
62
+ ```
63
+ sum_digits = sum(istr('9282334')) # answer 31
64
+ ```
65
+
66
+ The module is also a demonstration of extending a class (str) with additional and modified functionality.
67
+
68
+ ### Installation
69
+ Installing `istr` with pip is easy.
70
+ ```
71
+ pip install istr-python
72
+ ```
73
+ or when you want to upgrade,
74
+ ```
75
+ pip install istr-python --upgrade
76
+ ```
77
+ Alternatively, istr.py can be just copied into your current work directory from GitHub (https://github.com/salabim/istr).
78
+
79
+ No dependencies!
80
+
81
+ ### Usage
82
+ #### Start
83
+
84
+ Just start with
85
+
86
+ ```
87
+ import istr
88
+ ```
89
+
90
+ or the more conventional, more verbose:
91
+
92
+ ```
93
+ from istr import istr
94
+ ```
95
+
96
+ #### Use istr as int
97
+
98
+ We can define an istr, like:
99
+ ```
100
+ four = istr('4')
101
+ five = istr('5')
102
+ ```
103
+ The variables `four` and `five` can now be used as if they were int:
104
+
105
+ ```
106
+ twenty = four * five
107
+ ```
108
+ , after which twenty is `istr('20')`
109
+
110
+ The same can be done with
111
+
112
+ ```
113
+ twenty = 4 * five
114
+ ```
115
+
116
+ or
117
+
118
+ ```
119
+ twenty = four * 5
120
+ ```
121
+
122
+ And now `twenty` can be used as if it was an int as well. So
123
+
124
+ ```
125
+ twenty - four
126
+ ```
127
+
128
+ is `istr('16')`
129
+
130
+ We can do all the usual arithmetic operations on istrs, e.g.
131
+
132
+ ```
133
+ - four + (twenty / 2)
134
+ ```
135
+
136
+ is `istr('6')`
137
+
138
+ And we can test for equality. So:
139
+
140
+ ```
141
+ twenty == 20
142
+ ```
143
+ is True.
144
+
145
+ But istrs are actually strings! So
146
+
147
+ ```
148
+ twenty == '20'
149
+ ```
150
+
151
+ is also True!
152
+
153
+ For the order comparisons (<=, <, >, >=), an istr is always interpreted as an int.
154
+
155
+ That means that
156
+ ```
157
+ twenty < 30
158
+ twenty >= '10' # here '10' is converted to the integer 10 for the comparison
159
+ ```
160
+ are both`True`.
161
+
162
+ In contrast to an ordinary string
163
+ ```
164
+ print(four + five)
165
+ ```
166
+ prints `9`, as istr are treated as ints (if possible).
167
+
168
+ Please note that `four` could have also been initialized with
169
+ ```
170
+ four = istr(4)
171
+ ```
172
+ or even
173
+ ```
174
+ four, five = istr(4, 5)
175
+ ```
176
+
177
+ ##### Important
178
+ >All calculations are strictly integer calculations. That means that if a float or decimal variable is ever produced, it will be converted to an int.
179
+ > Also, divisions are always floor divisions!
180
+
181
+ #### Use istr as a string
182
+
183
+ We should realize that istrs are in fact strings.
184
+
185
+ To concatenate two istrs (or an istr and a str), we cannot use the `+` operator (remember `four + five` is `istr('9')`).
186
+
187
+ To concatenate strings, we use the or operator (`|`). So
188
+
189
+ ```
190
+ four | five
191
+ ```
192
+ will be `istr(`45`).
193
+
194
+ And
195
+ ```
196
+ (four | five) / 3
197
+ ```
198
+ is `istr('9')`.
199
+
200
+ To repeat a string in the usual sense, you cannot use the `*` operator (remember `3 * four` is `istr('12')`.
201
+
202
+ To repeat, we use the matrix multiplication operator (`@`). So
203
+
204
+ `3 @ four`
205
+
206
+ is `istr('444')`
207
+
208
+ And
209
+
210
+ ```four @ 3```
211
+
212
+ is also `istr('444')`
213
+
214
+ ##### Note
215
+
216
+ >
217
+ > It is not allowed to use the `@` operator for two istrs. So, `four @ five` raises a TypeError.
218
+ #### istr that can't be interpreted as an int
219
+
220
+
221
+ Although usually istrs are to be interpreted as an int, that's not a requirement.
222
+
223
+ So
224
+
225
+ ```
226
+ istr('abc')
227
+ ```
228
+
229
+ or
230
+
231
+ ```
232
+ istr('1,2,3')
233
+ ```
234
+
235
+ are perfectly acceptable.
236
+
237
+ However, we cannot perform any arithmetic or comparison operations with them.
238
+
239
+ If we try
240
+
241
+ ```
242
+ istr('abc') + 5
243
+ ```
244
+
245
+ a `TypeError` will be raised.
246
+
247
+ That holds for any arithmetic we try.
248
+
249
+ If we want to test if an istr can be interpreted (and thus used in an arithmetic and comparison expression). we can use the `is_int()` method. So
250
+
251
+ ```ìstr(20).is_int()```
252
+
253
+ is `True`, whereas
254
+
255
+ ```ìstr('abc').is_int()```
256
+
257
+ is `False`.
258
+
259
+
260
+
261
+ The `bool` operator works normally on the integer value of an istr. So
262
+
263
+ `bool(istr('0'))` ==> `False`
264
+
265
+ `bool(istr('1'))` ==> `True`
266
+
267
+ But if the istr can't be interpreted as an int, the string value will be used to test. So
268
+
269
+ `bool(istr('abc'))` ==> `True`
270
+
271
+ `bool(istr(''))` ==> `False`
272
+
273
+ #### Other operators
274
+
275
+ For the `in` operator, an istr is treated as an ordinary string, although it is possible to use ints as well:
276
+
277
+ ```
278
+ '34' in istr(1234)
279
+ 34 in istr(1234)
280
+
281
+ ```
282
+ On the left hand side an istr is always treated as a string:
283
+ ```
284
+ istr(1234) in '01234566890ABCDEF'
285
+ ```
286
+
287
+ Sorting a list of istrs is based on the integer value, not the string. So
288
+
289
+ ```
290
+ ' '.join(sorted('1 3 2 4 5 6 11 7 9 8 10 12 0'.split()))
291
+ ```
292
+
293
+ is
294
+
295
+ ```
296
+ '0 1 10 11 2 3 4 5 6 7 8 9'
297
+ ```
298
+
299
+ ,whereas
300
+
301
+ ```
302
+ ' '.join(sorted(istr('1 3 2 4 5 6 11 7 9 8 10 12 0'.split()))
303
+ ```
304
+
305
+ is
306
+
307
+ ```
308
+ '0 1 2 3 4 5 6 7 8 9 10 11'
309
+ ```
310
+ #### Using values that are neither string nor numeric to initialize istr
311
+
312
+ Apart from with numeric (to be interpreted as an int) or str, istr can be initialized with
313
+ several other types:
314
+
315
+
316
+ - if a dict (or subtype of dict), the same type dict will be returned with all *values* istr'ed
317
+ ```
318
+ istr({'one': 1, 'two':2}) ==> {'one': istr('1'), 'two': istr('2')}
319
+ ```
320
+
321
+ - if an iterator, the iterator will be mapped with istr
322
+ ```
323
+ mapped = (i for i in istr((i for i in range(2))))
324
+ print(mapped)
325
+ print(list(mapped))
326
+ ```
327
+ this wil print something like
328
+ ```
329
+ <generator object <genexpr> at 0x000002A10DE569B0>
330
+ [istr('0'), istr('1')]
331
+ ```
332
+
333
+ - if an iterable, the same type will be returned with all elements istr'ed
334
+
335
+ ```
336
+ istr([0, 1, 4]) ==> [istr('0'), istr('1'), istr('4')]
337
+ istr((0, 1, 4)) ==> (istr('0'), istr('1'), istr('4'))
338
+ istr({0, 1, 4}) ==> `{istr('4'), istr('0'), istr('1')} # or similar
339
+ ```
340
+
341
+ - if a range, an istr.range instance will be returned
342
+
343
+ ```
344
+ istr(range(3)) ==> istr.range(3)
345
+ list(istr(range(3))) ==> [istr('0'), istr('1'), istr('2')]
346
+ len(istr(range(3))) ==> 3
347
+ ```
348
+
349
+ - if an istr.range instance, the same istr.range will be returned
350
+ ```
351
+ istr(istr.range(5)) ==> istr.range(5)
352
+ ```
353
+
354
+
355
+ - if an istr, the same istr will be returned
356
+
357
+ ```
358
+ istr(istr('4')) ==> istr ('4')
359
+ ```
360
+
361
+ #### More than one parameter for istr
362
+ It is possible to give more than one parameter, in which case a tuple
363
+ of the istrs of the parameters will be returned, which can be handy
364
+ to unpack multiple values, e.g.
365
+
366
+ ```
367
+ a, b, c = istr(5, 6, 7) ==> a=istr('5') , b=istr('6'), c=istr('7')
368
+ ```
369
+ #### test for even/odd
370
+ It is possible to test for even/odd (provided the istr can be interpreted as an int) with the `is_even` and `is_odd` method, e.g.
371
+
372
+ ```
373
+ istr(4).is_even()) ==> True
374
+ istr(5).is_odd()) ==> True
375
+ ```
376
+ It is also possible to test for even/odd of an ordinary int:
377
+ ```
378
+ istr.is_even(4) ==> True
379
+ istr.is_odd(5) ==> True
380
+ ```
381
+ #### test for divisibility
382
+
383
+ It is possible to test whether an istr is divisible by a given value with the `is_divisible_by method,` e.g.
384
+
385
+ ```
386
+ istr(18).is_divisible_by(3) ==> True
387
+ istr(18).is_divisible_by(istr(3)) ==> True
388
+ istr(19).is_divisible_by(3) ==> False
389
+ istr(19).is_divisible_by(istr(3)) == False
390
+ ```
391
+ It is also possible to test for divisibility of an ordinary int:
392
+ ```
393
+ istr.is_divisible(18, 3) ==> True
394
+ istr.is_divisible(19, 3) ==> False
395
+ ```
396
+ The method `divided_by` not only tests divisibility, but also returns the result of the division. If not possible, None will be returned,
397
+ unless the *fallback* (last argument) is given, in which case *fallback* will be returned.
398
+ ```
399
+ istr(18).divided_by(3) ==> 6 (actually istr("6"))
400
+ istr(18).divided_by(istr(3)) ==> 6
401
+ istr(19).divided_by(3) ==> None
402
+ istr(19).divided_by(3, 0) ==>
403
+ istr(19).divided_by(3) ==> None
404
+ istr(19).divided_by(istr(3)) ==> None
405
+ istr.divided_by(18, 3) ==> 6
406
+ istr.divided_by(19, 3) ==> None
407
+ istr.divided_by(19, 3, 0) ==> 0
408
+ ```
409
+ #### test for square
410
+
411
+ It is possible to test whether the value is a perfect square (provided the istr can be interpreted as an int) with the `is_square` method, e.g.
412
+
413
+ ```
414
+ istr(4).is_square() ==> True
415
+ istr(5).is_square()) ==> False
416
+ ```
417
+ It is also possible to test for square of an ordinary int:
418
+ ```
419
+ istr.is_square(4) ==> True
420
+ istr.is_square(5) ==> False
421
+ ```
422
+ #### test for cube
423
+
424
+ It is possible to test whether the value is a perfect cube (provided the istr can be interpreted as an int) with the `is_cube` method, e.g.
425
+
426
+ ```
427
+ istr(27).is_cube() ==> True
428
+ istr(28).is_cube()) ==> False
429
+ ```
430
+ It is also possible to test for cube of an ordinary int:
431
+ ```
432
+ istr.is_cube(27) ==> True
433
+ istr.is_cube(28 ==> False
434
+ ```
435
+
436
+ #### test for power of
437
+
438
+ It is possible to test whether the value is a perfect power of a given exponent (provided the istr can be interpreted as an int) with the `is_power_of` method, e.g.
439
+
440
+ ```
441
+ istr(81).is_power_of(4) ==> True
442
+ istr(82).is_power_of(4) ==> False
443
+ ```
444
+ It is also possible to test for power of of an ordinary int:
445
+ ```
446
+ istr.is_power_of(81, 4) ==> True
447
+ istr.is_power_of(82, 4) ==> False
448
+ ```
449
+
450
+ #### test for prime
451
+
452
+ It is possible to test whether the value is a prime number (provided the istr can be interpreted as an int) with the `is_prime` method, e.g.
453
+
454
+ ```
455
+ istr(4).is_prime() ==> False
456
+ istr(5).is_prime()) ==> True
457
+ ```
458
+ It is also possible to test for prime of an ordinary int:
459
+
460
+ ```
461
+ istr.is_prime(4) ==> False
462
+ istr.is_prime(5) ==> True
463
+ ```
464
+
465
+ #### test whether all characters are distinct
466
+
467
+ With the `all_distinct` method, it is possible to test whether all characters are distinct (i.e. no character appears more than once).
468
+
469
+ ```
470
+ istr('01234').all_distict() ==> True
471
+ istr('012340').all_distict() ==> False
472
+ n98 = istr(98)
473
+ n100 = n98 + 2
474
+ istr(n98).all_distinct() ==> True
475
+ istr(n100).all_distinct() ==> False
476
+ ```
477
+ #### test if characters are consecutive
478
+
479
+ With the `is_consecutive` method, it is possible to test whether the individual digits (characters) are consecutive.
480
+ ```
481
+ istr(123).is_consecutive() ==> True
482
+ istr(124).is_consecutive() ==> False
483
+ ```
484
+
485
+ Note that this method can also be used for non-istr-s, like `istr.is_consecutive(123) ==> True`
486
+
487
+ #### test for triangular number
488
+
489
+ With the `is_triangular` method, it is possible to test whether this is a triangular number (sum of integers, starting at 1):
490
+
491
+ ```
492
+ istr(6).is_triangular() ==> True
493
+ istr(7).is_triangular() ==> False
494
+ ```
495
+
496
+ Note that this method can also be used for non-istr-s, like `istr.is_triangular(6) ==> True`.
497
+
498
+
499
+ #### reverse an istr
500
+
501
+ The method `reversed()` will return an istr with the reversed content:
502
+ ```
503
+ istr(456).reversed() ==> istr('654')
504
+ istr('0456').reversed() ==> istr('6540')
505
+ ```
506
+ The same can, of course, be achieved with
507
+ ```
508
+ istr(456)[::-1] ==> istr('654')
509
+ istr('0456')[::-1] ==> istr('6540')
510
+ ```
511
+ ##### Note
512
+ >
513
+ > It is possible to reverse a negative istr, but the result can't be interpreted as an int anymore.
514
+ >
515
+ > ```
516
+ > istr(-456).reversed() + 3 ==> TypeError
517
+ > ```
518
+
519
+ #### enumerate with istrs
520
+
521
+ The `istr.enumerate` class method can be used just as the built-in enumerate function.
522
+ The iteration counter however is an istr rather than an int. E.g.
523
+
524
+ ```
525
+ for i, c in istr.enumerate('abc'):
526
+ print(f'{repr(i)} {c}')
527
+ ```
528
+ prints
529
+ ```
530
+ istr('0') a
531
+ istr('1') b
532
+ istr('2') c
533
+ ```
534
+ #### join with istrs
535
+
536
+ `istr.join` can be used just like `str.join`. The result will be an istr.
537
+
538
+ On top of that, `istr.join` may be used as a class method, like
539
+
540
+ - `istr.join(("1", "2", "3"))` ==> `istr("123")` (`""` is applied as separator)
541
+ - `istr.join("0", ("1", "2", "3"))` ==> `istr("10203")`)
542
+
543
+ #### itertools with istrs
544
+
545
+ All methods in itertools are also available directly from istr.
546
+ Note that the result is istr-ed (apart from groupby and tee).
547
+
548
+ The following class methods are supported (provided their counterpart exists in the installed Python version's itertools):
549
+
550
+ - istr.accumulate
551
+ - istr.chain
552
+ - istr.combinations
553
+ - istr.combinations_with_replacement
554
+ - istr.compress
555
+ - istr.count
556
+ - istr.cycle
557
+ - istr.dropwhile
558
+ - istr.filterfalse
559
+ - istr.groupby (not istr-ed)
560
+ - istr.islice
561
+ - istr.pairwise
562
+ - istr.permutations
563
+ - istr.product
564
+ - istr.repeat
565
+ - istr.starmap
566
+ - istr.takewhile
567
+ - istr.tee (not istr-ed)
568
+ - istr.zip_longest
569
+
570
+ This can be handy as these methods don't have to be imported from itertools anymore.
571
+
572
+ All methods have exactly the same (optional) parameters as their itertools counterpart.
573
+
574
+ For example:
575
+
576
+ ```
577
+ list(istr.repeat(1, 4)) ==> [istr('1'), istr('1'), istr('1'), istr('1')]
578
+ next(istr.count(3)) ==> istr('3')
579
+ ```
580
+
581
+ One more example:
582
+
583
+ ```
584
+ for t in istr.permutations(range(3)):
585
+ print(t)
586
+ ```
587
+ results in
588
+ ```
589
+ (istr('0'), istr('1'), istr('2'))
590
+ (istr('0'), istr('2'), istr('1'))
591
+ (istr('1'), istr('0'), istr('2'))
592
+ (istr('1'), istr('2'), istr('0'))
593
+ (istr('2'), istr('0'), istr('1'))
594
+ (istr('2'), istr('1'), istr('0'))
595
+ ```
596
+
597
+ #### concatenate an iterable
598
+
599
+ The `istr.concat` method can be useful to map all items of an iterable
600
+ to `istr` and then concatenate these.
601
+
602
+ `
603
+
604
+ ```
605
+ list(istr.concat(((1,2),(3,4))) ==> istr([12,34])
606
+ list(istr.concat(istr.permutations(range(3),2))) ==>
607
+ [istr('01'), istr('02'), istr('10'), istr('12'), istr('20'), istr('21')]
608
+ ```
609
+
610
+ #### prod to get product of an iterable
611
+
612
+ The method `prod` can be used to return the product of an iterable (including an istr), like `math.prod`, but as istr.
613
+ Thus, `istr.prod(range(1,5))` is `istr(24)`
614
+ And `istr("123", start=4)` is also `istr(24)`.
615
+
616
+ It is also possible to apply `prod` on an istr:
617
+ `istr(1234).prod()` is `istr(24)`
618
+ `istr("123").prod(start=4)` is `istr(24)`
619
+
620
+ #### sumprod to get the sum of products of iterables
621
+
622
+ The class method `istr.sumprod()`, is equivalent to `math.sumprod()`, but applies istr to both iterables.
623
+ Note that this method is available even in Python < 3.12 .
624
+ Thus, `istr.sumprod("12", (3,4))` is `istr(11)`
625
+ In contrast to `math.sumprod()`, `istr.sumprod()` supports a `strict` parameter (True by default)
626
+ Thus, `istr.sumprod("12", (3,4,5), strict=False)` is `istr(11)`, whereas `istr.sumprod("12", (3,4,5))`
627
+ raises a ValueError.
628
+
629
+ #### get all squares, cubes, power ofs or primes in a given range
630
+
631
+ The class methods `istr.squares`, `istr.cubes` and `istr.primes` can be used to get a list of all squares, cubes or primes up to a given upperbound (non inclusive) or between a given lowerbound and upperbound (non inclusive), like:
632
+
633
+ `istr.squares (100)` returns a list of all squares <100
634
+ `istr.squares(50, 100)` return a list of all squares >=50 and <100
635
+
636
+ Unless `cache=False` is specified, the result of the query is cached.
637
+
638
+ The same functionality is available for cubes, power ofs and primes
639
+
640
+ #### generate istr with digits
641
+
642
+ The class method `digits` can be used to return an istr of digits according to a given specification.
643
+ The method takes either no or a number of arguments.
644
+
645
+ If no arguments are given, the result will be `istr('0123456789')`.
646
+
647
+ The given argument(s) result in a range of digits.
648
+
649
+ - `<n>` ==> n
650
+ - `<n-m>` ==> n, n+1, ..., m
651
+ - `-n>` ==> 0, 1, ... n
652
+ - `n->` ==> n, n+1, ..., 9 if n is numeric (0-9), n, n+1, ... Z if n is a letter
653
+ - `'-'` ==> 0, 1, ..., 9
654
+ - `''` ==> 0, 1, ..., 9
655
+
656
+ (n and m must be digits between 0 and 9 or letters letters between A and Z)
657
+
658
+ When no stop value is specified, it will be
659
+
660
+ * 9 if the start value is between 0 and 9
661
+ * Z if the start value is between A and Z
662
+
663
+ The final result is an istr composed of the given range(s).
664
+
665
+ Here are some examples:
666
+
667
+ ```
668
+ istr.digits() ==> istr('0123456789')
669
+ istr.digits('') ==> istr('0123456789')
670
+ istr.digits('1') ==> istr('1')
671
+ istr.digits('3-') ==> istr('3456789')
672
+ istr.digits('-3') ==> istr('0123')
673
+ istr('1-4', '6', '8-9') ==> istr('1234689')
674
+ istr('1', '1-2', '1-3') ==> istr('11213')
675
+ istr.digits('-z') ==> istr('0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ')
676
+ istr.digits('A-F') ==> istr('ABCDEF')
677
+ istr.digits('C') ==> istr('C')
678
+ istr.digits('3-') ==> istr('34567879')
679
+ istr.digits('X-') ==> istr('XYZ')
680
+ ```
681
+
682
+ #### Decomposing to and composing from letter variables
683
+
684
+ When we have an istr, we can decompose the value into individual one letter (global) variables with the `decompose()` method.
685
+ E.g.
686
+
687
+ ```
688
+ istr(485).decompose("abc")
689
+ ```
690
+ will set the global variables `a`, `b` and `c` to be set to `istr(4)`. `istr(8)` and` istr(5)`.
691
+ Note that the length of the letters specifier must be the same as the length of the istr. Furthermore, multiple values for the same variables result in a ValueError.
692
+
693
+ To decompose an istr into individual variables, it is arguably easier and safer to unpack the istr, like
694
+
695
+ ```
696
+ a, b, c = istr(485)
697
+ ```
698
+
699
+ With `istr.compose()`, an istr can be constructed from individual (global) variables and digits.
700
+ E.g.
701
+
702
+ ```
703
+ x = 3
704
+ y = 9
705
+ z = 6
706
+ test1 = istr.compose("xyz")
707
+ test2 = istr.compose("xyz0")
708
+ ```
709
+ Now, `test1` will be `istr(396)` and `test2` will be `istr(3960)`.
710
+
711
+ Composing can also be done by prefixing a string with '=', like:
712
+
713
+ ```
714
+ test1 = istr("=xyz")
715
+ test2 = istr("=xyz0")
716
+
717
+ Now, `test1` will be `istr(396)` and `test2` will be `istr(3960)`.
718
+ ```
719
+ Note that `str(istr("="))` is "=".
720
+
721
+ Composing and assignment can be done by prefixing a string with ':=', like:
722
+
723
+ ```
724
+ if istr(":=xyz") > 300:
725
+ print(f"{xyz=}") # ==> will print xyz=396
726
+ ```
727
+ Note that `str(istr(":="))` is ":=" and does not assign any value.
728
+
729
+ Usually, composing and decomposing uses the globals namespace, but this can be overridden with the namespace parameter. See the test suite for details.
730
+
731
+ #### Subclassing istr
732
+
733
+ When a class is derived from istr, all methods will return that newly derived class.
734
+
735
+ E.g.
736
+ ```
737
+ class jstr(istr.type):
738
+ ...
739
+
740
+ print(repr(jstr(4) * jstr(5)))
741
+ ```
742
+ will print `jstr('20')`
743
+
744
+ #### Changing the way repr works
745
+
746
+ It is possible to control the way an `istr` instance will be repr'ed.
747
+
748
+ By default, `istr(5)` is represented as `istr('5')`.
749
+
750
+ With the `istr.repr_mode()` context manager, that can be changed:
751
+ ```
752
+ with istr.repr_mode('str'):
753
+ five = istr(5)
754
+ print(repr(five))
755
+ with istr.repr_mode('int'):
756
+ five = istr(5)
757
+ print(repr(five))
758
+ with istr.repr_mode('istr'):
759
+ five = istr(5)
760
+ print(repr(five))
761
+ ```
762
+ This will print
763
+ ```
764
+ '5'
765
+ 5
766
+ istr('5')
767
+ ```
768
+ If the repr_mode is `'int'` and the istr can't be interpreted as an int the string `?` will be returned:
769
+
770
+ ```
771
+ with istr.repr_mode('int'):
772
+ abc = istr('abc')
773
+ print(repr(abc))
774
+ ```
775
+
776
+ This will print
777
+
778
+ ```
779
+ ?
780
+ ```
781
+
782
+ ##### Note
783
+ >
784
+ > The way an `istr` is represented is determined at initialization.
785
+
786
+ It is also possible to set the repr mode without a context manager:
787
+
788
+ ```
789
+ istr.repr_mode('str')
790
+ five = istr('5')
791
+ print(repr(five))
792
+ ```
793
+ This will print
794
+ ```
795
+ '5'
796
+ ```
797
+ Finally, the current repr mode can be queried with `istr.repr_mode()`. So upon start:
798
+ ```
799
+ print(repr(istr.repr_mode()))
800
+ ```
801
+ will output `istr`.
802
+
803
+ #### Changing the base system
804
+
805
+ By default, `istr` works in base 10. However it is possible to change the base system with the `istr.base()` context manager / method.
806
+
807
+ Any base between 2 and 36 may be used.
808
+
809
+ Note that the integer is **always** stored in base 10 mode, but the string
810
+ representation will reflect the chosen base (at time of initialization).
811
+
812
+ Some examples:
813
+ ```
814
+ with istr.base(16):
815
+ a = istr('7fff')
816
+ print(int(a))
817
+
818
+ b = istr(127)
819
+ print(repr(b))
820
+ ```
821
+ This will result in
822
+ ```
823
+ 32767
824
+ istr('7F')
825
+ ```
826
+ All calculations are done in the decimal 10 base system.
827
+
828
+ Note that the way an `istr` is interpreted is determined at initialization.
829
+
830
+ It is also possible to set the repr mode without a context manager:
831
+ ```
832
+ istr.base(16)
833
+ print(int(istr('7fff')))
834
+ ```
835
+ This will print
836
+ ```
837
+ 32767
838
+ ```
839
+ Finally, the current base can be queried with `istr.base()`, so upon start:
840
+ ```
841
+ print(istr.base())
842
+ ```
843
+ will result in `10`.
844
+
845
+ #### Changing the format of the string
846
+
847
+ When an istr is initialized with a string the istr will be always stored as such.
848
+
849
+ ```
850
+ repr('4')) ==> istr('4')
851
+ repr(' 4')) ==> istr(' 4')
852
+ repr('4 ')) ==> istr('4 ')
853
+ ```
854
+
855
+ For initializing with an int (or other numeric) value, the string is by default simply the str representation
856
+
857
+ ```
858
+ repr(4)) ==> istr('4')
859
+ ```
860
+
861
+ With the `istr.int_format()` context manager this behavior can be changed.
862
+ If the format specifier is a number, most likely a single digit, that
863
+ will be the minimum number of characters in the string:
864
+
865
+ ```
866
+ with istr.int_format('3'):
867
+ print(repr(istr(1)))
868
+ print(repr(istr(12)))
869
+ print(repr(istr(123)))
870
+ print(repr(istr(1234)))
871
+ ```
872
+ will print
873
+ ```
874
+ istr(' 1')
875
+ istr(' 12')
876
+ istr('123')
877
+ istr('1234')
878
+ ```
879
+ If the string starts with a `0`, the string will be zero filled:
880
+ ```
881
+ with istr.int_format('03'):
882
+ print(repr(istr(1)))
883
+ print(repr(istr(12)))
884
+ print(repr(istr(123)))
885
+ print(repr(istr(1234)))
886
+ ```
887
+ will print
888
+ ```
889
+ istr('001')
890
+ istr('012')
891
+ istr('123')
892
+ istr('1234')
893
+ ```
894
+
895
+ ##### Note
896
+ >
897
+ > For bases other than 10, the string will never be reformatted!
898
+
899
+ ### Overview of operations
900
+
901
+ The table below indicates whether the string or integer version of istr is applied.
902
+
903
+ ```
904
+ operator/function int str Example
905
+ -----------------------------------------------------------------------------------------
906
+ + x istr(20) + 3 ==> istr('23')
907
+ _ x istr(20) - 3 ==> istr('17')
908
+ * x istr(20) * 3 ==> istr('60')
909
+ / x istr(20) / 3 ==> istr('6')
910
+ // x istr(20) // 3 ==> istr('6')
911
+ % x istr(20) % 3 ==> istr('2')
912
+ divmod x divmod(istr(20), 3) ==> (istr('6'), istr('2'))
913
+ ** x istr(2) ** 3 ==> istr('8')
914
+ <=, <, >, >= x istr('100') > istr('2') ==> True
915
+ abs x abs(istr(-20)) ==> istr('20')
916
+ int x int(istr("20")) ==> 20
917
+ float x float(istr("20")) ==> 20.0
918
+ complex x complex(istr("20")) ==> (20+0j)
919
+ == x x istr(20) == 20 ==> True | istr(20) == '20' ==> True
920
+ bool x x *) bool(istr(' 0 ')) ==> False | bool(istr('')) ==> False
921
+ @ x istr(20) @ 3 ==> istr('202020')
922
+ | x istr(20) | '5' ==> istr('205')
923
+ slicing x istr(12345)[1:3] ==> istr('23')
924
+ iterate x [x for x in istr(20)] ==> [istr('2'), istr('0')]
925
+ len x len(istr(' 20 ')) ==> 4
926
+ count x istr(100).count('0') ==> 2
927
+ index x istr(' 100 ').index('0') ==> 2
928
+ split x istr('1 2').split() ==> (istr('1'), istr('2'))
929
+ string format x f"|{istr(1234):6}|" ==> '|1234 |'
930
+ other string methods x istr('aAbBcC').lower() ==> istr('aabbcc')
931
+ istr('aAbBcC').islower() ==> False
932
+ istr(' abc ').strip() ==> istr('abc')
933
+ ...
934
+ -----------------------------------------------------------------------------------------
935
+ *) str is applied if is_int() is False
936
+ ```
937
+ ### Test script
938
+ There's an extensive pytest script in the `\tests` directory.
939
+
940
+ This script also shows clearly the ways istr can be used, including several edge cases. Highly recommended to have a look at.
941
+
942
+ ### Contact info
943
+
944
+ You can contact Ruud van der Ham, the core developer, via ruud@salabim.org .
945
+
946
+ ### Badges
947
+ ![PyPI](https://img.shields.io/pypi/v/istr-python) ![PyPI - Python Version](https://img.shields.io/pypi/pyversions/istr-python) ![PyPI - Implementation](https://img.shields.io/pypi/implementation/istr-python)
948
+ ![PyPI - License](https://img.shields.io/pypi/l/istr-python) ![Black](https://img.shields.io/badge/code%20style-black-000000.svg)
949
+ ![GitHub last commit](https://img.shields.io/github/last-commit/salabim/istr)