Introduced istr.is_increasing(), istr.is_decreasing(), istr.is_non_increasing() and istr.is_non_decreasing()
So:
xxxxxxxxxxistr(1223).is_increasing() ==> Falseistr(1223).is_non_decreasing() ==> Trueistr(3221).is_decreasing() ==> Falseistr(3221).is_non_increasing ==> True
It is also possible to test for 'increasingness' and friends for anything that can be converted to a str:
xxxxxxxxxxistr.is_increasing(123) ==> True
In contrast to the readme, istr.count did not work on istr, but instead used always the itertools count.
This has been fixed. So, now it is possible to do:
xxxxxxxxxxistr(100).count(0) ==> 2istr(100).count(0, 1) ==>2istr(100).count("a") ==> 0
If called like istr.count(), the itertools version is used:
xxxxxxxxxxistr.count() ==> istr('0'), istr('1'), istr('2'), ...istr.count(10) ==> istr('10'), istr('11'), istr('12'), ...istr.count(10,3) ==> istr('10'), istr('13'), istr('16'), ...
Note that istr.count(istr(10)) results in istr('10'), istr('11'), istr('12'), ..., but
istr.count(istr(10),1) is in fact the str version and thus returns 1.
Introduced istr.is_palindrome() to check whether an istr is palindromic:
xxxxxxxxxxistr(12321).is_palindrome() ==> Trueistr('aba').is_palindrome() ==> Trueistr(123).is_palindrome() ==> False
It is also possible to test for a palindrome for anything that can be converted to a str:
xxxxxxxxxxistr.is_palindrome(121) ==> Trueistr.is_palindrome('no devil lived on') ==> Trueistr.is_palindrome(min) ==> False
Internal change: caller frame now assessed via the new 'standard' function real_caller_frame()
__new__ reorganized (now uses match/case)
an istr can now be initialized with any expression, even if it can't be evaluated as an int, like 5 + 6j or min . Or course, these istr-s can't be used as int.
the repr of an istr is not set upon initialization, but rather be constructed when required, resulting in faster initialization.
getting namespace is now more reliable.
Python 3.10 is now a minimum requirement (because match/case statements are now used).
istr.primes did not work properly for lower or upper bounds that were an istr. Fixed.
optimized istr,primes, istr.squares, istr.cubes and istr.power_ofs for upper bounds up to 1_000_000 .
the start parameter of istr.enumerate could not be an istr. Fixed.
istr.power_ofs now correctly supports negative lower and upper bounds.
istr.is_power_of now correctly supports negative values.
istr.compose now also accepts digits. So
xxxxxxxxxxa=1b=2print(istr.compose('ab3'))print(istr('=a9'))print(istr('=9a'))print(istr(':=a9'))print(a9)print(istr(':=9a'))
will print
xxxxxxxxxx12319911919ValueError: '9a' is not a valid identifier
istr.divided_byhas a new parameter, fallback which will be retured if the (integer) division is not possible. The default is None.
xxxxxxxxxxistr(19).divided_by(3) ==> Noneistr(19).divided_by(3, 0) ==> 0
introduced istr.divided_by, which will return None if not divisible by the given divisor, otherwise the result of the division. Example:
xxxxxxxxxxistr(18).divided_by(3) ==> 6istr(19).divided_by(3) ==> None
So, it combines is_divisible_by and the actual division.
some internal changes and added tests
introduced istr.power_ofs, which can be used to get all numbers up to a given upperbound or between a given lowerbound and upperbound that are a power of a given number, like
istr.power_ofs (4, 100) returns [istr('0'), istr('1'), istr('16'), istr('81')]
istr.squares and istr.cubes now delegate to istr.power_ofs
istr.primes, istr.squares, istr.cubes and istr.power_ofs now have a keyword argument cache, which is True by default. If False, the result is not cached.
istr.primes, istr.squares, istr.cubes and istr.power_ofs now have a non inclusive upper bound (in line with Python's common behaviour),
xxxxxxxxxxistr.squares(16) ==> [istr('0'), istr('1'), istr('4'), istr('9')] # 16 is excluded
istr.is_divisible_by now correctly returns False if called with a 0 as divisor.
error message for istr.compose and istr.decompose improved.
istr.squares, istr.cubes and istr.primes now caches the result, so it's no problem to call multiple times. The caching can be disabled with the cache=False parameter
istr.is_square, istr.is_cube and istr.is_prime now uses a (once) precomputed set for numbers <=1_000_000, thus improving performance in many cases.
Introduced istr.squares , istr.cubes and istr.primes .to get all squares, cubes or primes up to a given upperbound or between a given lowerbound and upperbound:
istr.squares (100) returns a list of all squares <=100
istr.squares(50, 100) return a list of all squares >=50 and <=100
The same functionality is available for cubes and primes
istr.join may now also be used as a class method, like
istr.join(("1", "2", "3")) ==> istr("123") ("" is applied as separator)
istr.join("0", ("1", "2", "3")) ==> istr("10203"))
Tests for this new functionality have been added.
Introduced is_consecutive()
This method checks whether all (string) elements of an istr are consecutive (distance 1):
istr(123).is_consecutive() is True, whereas istr(124).is_consecutive() is False.
Note that this method can also be used for non-istr-s, like istr.is_consecutive(123)
Introduced is_triangular()
This method checks whether a number is a triangular number:
istr(6).is_triangular() is True, whereas istr(7).is_triangular() is False.
Note that this method can also be used for non-istr-s, like istr.is_triangular(6)
A new way to compose an istr from global one-letter variables is introduced: by starting a string with := as an argument to istr, the rest of the argument will be used to compose the istr from the one-letter variables, just like when the string started with =. But, now, the evaluated will also be assigned to a variable composed of the names of the one-letter variables. E.g.
xxxxxxxxxxx=4y=7if istr(":=xy").is_prime():print(f"{xy=}")
This will print xy=47. This is particularly useful when combined with peek.
From now on, when istr() is applied to an istr, the current base, repr_mode and int_format will be used to determine the representation. This can be handy to reformat an istr.
istr now has three more keyword arguments: base, int_format and repr_mode. So these attributes can now be set easily on an individual instance.
So, repr(istr(12, base==36)) is istr('C')
istr.range now has three more keyword arguments: base, int_format and repr_mode.
So, list(istr.range(4, base=2)) is [istr('0', istr('1'), istr('10'), istr('11')
The base, int_format and repr_mode of an istr can now be queried with the methods this_base(), this_int_format and this_repr_mode. E.g.
istr(12, base=36).this_base() is 36 and
The builtins float() and complex() now support istr-s as well.
The namespace keyword argument now propagates to embedded istr-s. So, for instance
xxxxxxxxxxx, y, z = 1, 2, 3istr(["=xy", "=yz"])
evaluates to [istr("12"), istr("23")]
And
xxxxxxxxxxistr(["=xy", "=yz"], namespace=dict(x=3, y=4, z="z")
evaluates to [istr("34"), istr("4z")]
For more examples, see the test suite.
istr("=") now evaluates to an istr with one = character, rather than compose to an empty istr.
In the test suite, the variables minus_one to thirteen are now explicitly defined, instead of via a clever patch loop. This is to avoid excessive ruff warnings reported.
Bug in istr.__eq__() made that peek crashed when peeking a non-int istr. Fixed.
Introduced istr.prod(), which is equivalent to math.prod(), but results in an istr.
Thus, istr.prod(range(1,5)) is istr(24)
And istr.prod((1,2,3), start=4) is also istr(24).
It is also possible to apply prod on an istr:
istr(1234).prod() is istr(24)
istr("123").prod(start=4) is istr(24)
Introduced istr.sumprod(), which is equivalent to math.sumprod(), but applies istr to both iterables.
Note that this method is available even in Python < 3.12 .
Thus, istr.sumprod("12", (3,4)) is istr(11)
In contrast to math.sumprod(), istr.sumprod() supports a strict parameter (True by default)
Thus, istr.sumprod("12", (3,4,5), strict=False) is istr(11), whereas istr.sumprod("12", (3,4,5))
raises a ValueError.
Python 3.7 is no longer supported. So, from now on Python >= 3.8 is required.
A new way to compose an istr from global one-letter variables is introduced: by starting a string with = as an argument to istr, the rest of the argument will be used to compose the istr from the one-letter variables:
xxxxxxxxxxx=4y=7z=0assert istr("=xyz") == istr.compose("xyz")
Not so much a change in istr, but a remark: To decompose an istr into individual variables, istr.decompose() can be used, But it is arguably easier and safer to unpack the istr, like
xxxxxxxxxxa, b, c = istr(934)
, which is functionally equivalent to
xxxxxxxxxxistr(934).decompose("abc")
Refactored istr.is_square(), istr.is_cube(), istr.is_power_of(), istr.is_odd(), istr.is_even(), istr.is_prime() and istr.is_disible_by().
Added istr.is_cube() and istr.is_power_of().
Internal change: is_square() now delegates to istr._is_power_off, which is used also for istr.is_cube() and istr.is_power_of().
Introduced compose and decompose methods.
With decompose, one-letter global variables can be set from an istr, e.g.
xxxxxxxxxxistr(934).decompose("abc")
will result in the global variables a=9, b=3 and c=4.
With compose, an istr will be constructed based on the values of one letter global variables, e.g.
xxxxxxxxxxx=4y=7z=0s = istr.compose("xyz")
will assign istr(470) to s.
Readme updated
At last, the short form of importing (import istr) works properly!
The new way of importing istr didn't work properly. So, importing should be done with from istr import istr (again).
With this version is possible to just use
import istr
instead of
from istr import istr
All functionality is maintained.
The only functional difference is that istr cannot be use as a type in subclassing anymore. In order to still be able to subclass, use istr.type instead. So
xxxxxxxxxxclass jstr(istr.type):...
The methods istr.is_even, istr.is_odd, istr.is_square, istr.is_prime and istr.is_divisible_by can now also be used with an ordinary int. E.g.:
xxxxxxxxxxistr.is_even(4) ==> Trueistr.is_odd(4) ==> Falseistr.is_square(4) ==> Trueistr.is_prime(4) ==> Falseistr.is_divisible_by(4, 2) ==> True
Introduced istr.is_square and istr.is_prime methods. Examples:
xxxxxxxxxxistr(4).is_square() ==> Trueistr(5).is_square()) ==> Falseistr(4).is_prime() ==> Falseistr(5).is_prime()) ==> True
Introduced istr.is_divisible_by method.
For example:
xxxxxxxxxxistr(18).is_divisible_by(3) ==> Trueistr(18).is_divisible_by(istr(3)) ==> Trueistr(19).is_divisible_by(3) ==> Falseistr(19).is_divisible_by(istr(3)) == False
Instead of using istr in the internal methods _int_method and _str_method, the proper class is now used. This makes inheriting from istr more correct.
When comparing istrs with <=, <, > and >= the type of the result is now bool, instead of istr, which is more logical.
All methods in itertools are now available directly from istr. For example:
xxxxxxxxxxlist(istr.repeat(1, 4)) ==> [istr('1'), istr('1'), istr('1'), istr('1')]next(istr.count(3)) ==> istr('3')
This can be handy as these methods don't have to be imported from itertools anymore.
Added tests for these new class methods.
Serious bug with caching istr.digits() fixed. Added tests for properly caching istr.digits().
Serious bug with the or (|) operator fixed. Added tests for the or (|) operator.
The class istr.range is now immutable.
The dunder int methods that were previously all defined separately are now defined with a simple loop that uses partialmethod (this does not change the functionality in any way).
The string methods that were previously all defined separately are now defined with a simple loop that uses partialmethod (this does not change the functionality in any way).
Generating TypeError messages for incompatible types is now direct (via _frepr method) instead of with a tricky force_repr boolean.
The in operator now relies on the contains method inherited from str (in other words it is not overridden anymore).
When a string that can't be interpreted as an int is created when istr.repr_mode is 'int', the repr of that file will be ? (was: nan):
xxxxxxxxxxwith istr.repr_mode('int'):a = istr('abc')print(a)
will print
xxxxxxxxxx?
From now on, the changelog is not anymore part of the istr.py file, but is in a separate changelog.md file.
istr.digits now also supports the letters from A through Z, making it possible to generate digits for bases >10.
xxxxxxxxxxistr.digits('-z') ==> istr('0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ')istr.digits('A-F') ==> istr('ABCDEF')istr.digits('C') ==> istr('C')
Note that the default stop value is 9 when the start is a numeric digit. If start is a letter, the default stop value is Z. So
xxxxxxxxxxistr.digits('3-') ==> istr('34567879')istr.digits('X-') ==> istr('XYZ')
Technical detail: caching digits is now implemented with a custom cache dict instead of lru_cache to be able to include _base, _int_format and _int_repr_mode in the key.
Introduced a new method: all_distinct.
This can be handy for quite a few puzzles.
xxxxxxxxxxistr('01234').all_distinct() ==> Trueistr('012340').all_distint() ==> Falseistr('thequickbrown').all_distinct() ==> True
With this version, istrs do not have to be interpretable as an int anymore. Only when arithmetic and friends are to be carried with an istr, that's a requirement.
So now we can say
xxxxxxxxxxa = istr('1 2 3')print(a.split())
and get
xxxxxxxxxx[istr('1'), istr('2'), istr('3')]
But
xxxxxxxxxxa = istr('1 2 3')b = a + 1
will raise
xxxxxxxxxxTypeError: unsupported operand for +: istr('1 2 3') and 1
It is possible to check whether an istr can be interpreted as an int with the is_int method:
xxxxxxxxxxa = istr('1 2 3')print(a.is_int())
will give
xxxxxxxxxxFalse
This also means that there is no reason for istr('') to be interpreted as 0. So it isn't anymore.
And reversed() now also works with negative numbers, although the result can't be used in calculations.
The method / context manager format has been renamed to int_format.
The bool method now operates on the string if it can not be interpreted as an int.
That means that bool(istr('')) is False. For any other istr where is_int() is True, bool will be True.
Added __iter__ method .
So now,
xxxxxxxxxxfor c in istr('123'):...
results in c values that are istrs
Added istr.digits method:
xxxxxxxxxxistr.digits() ==> istr('0123456789')istr.digits('') ==> istr('0123456789')istr.digits('1') ==> istr('1')istr.digits('3-') ==> istr('3456789')istr.digits('-3') ==> istr('0123')istr('1-4', '6', '8-9') ==> istr('1234689')istr.digits('1', '1-2', '1-3') ==> istr('112123')
Note that a digit can occur more than once.
Added all relevant string methods to return istrs or data structures with istrs.
Added corresponding tests.
Changed the way istr.range is implemented.
Changed the context manager istr.format() to be used directly without the with statement.
Also, now istr.format() works without any argument and then returns the current format.
istr class now uses __slots__.
All internal values and methods now start with an underscore.
Introduced istr.repr_mode()
Introduced istr.base()
Extended tests for new functionality
initial version with changelog