Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Incorporating float.is_integer into Decimal #70867

Closed
rob-smallshire mannequin opened this issue Mar 31, 2016 · 67 comments
Closed

Incorporating float.is_integer into Decimal #70867

rob-smallshire mannequin opened this issue Mar 31, 2016 · 67 comments
Assignees
Labels
3.11 only security fixes stdlib Python modules in the Lib dir type-feature A feature request or enhancement

Comments

@rob-smallshire
Copy link
Mannequin

rob-smallshire mannequin commented Mar 31, 2016

BPO 26680
Nosy @tim-one, @rhettinger, @tiran, @serhiy-storchaka, @rob-smallshire, @abingham
PRs
  • bpo-26680: Incorporate is_integer in all built-in and standard library numeric types #6121
  • Revert "bpo-26680: Incorporate is_integer in all built-in and standard library numeric types" #22584
  • Files
  • is_integer_numeric_tower.patch: Patch to introduce is_integer to the numeric tower
  • is_integer_decimal.patch: Patch introducing is_integer to Decimal
  • Note: these values reflect the state of the issue at the time it was migrated and might not reflect the current state.

    Show more details

    GitHub fields:

    assignee = 'https://github.com/rhettinger'
    closed_at = <Date 2021-05-06.20:54:14.940>
    created_at = <Date 2016-03-31.19:34:24.187>
    labels = ['type-feature', 'library', '3.11']
    title = 'Incorporating float.is_integer into Decimal'
    updated_at = <Date 2021-05-06.20:54:14.939>
    user = 'https://github.com/rob-smallshire'

    bugs.python.org fields:

    activity = <Date 2021-05-06.20:54:14.939>
    actor = 'rhettinger'
    assignee = 'rhettinger'
    closed = True
    closed_date = <Date 2021-05-06.20:54:14.940>
    closer = 'rhettinger'
    components = ['Library (Lib)']
    creation = <Date 2016-03-31.19:34:24.187>
    creator = 'robert_smallshire'
    dependencies = []
    files = ['42335', '42336']
    hgrepos = []
    issue_num = 26680
    keywords = ['patch']
    message_count = 67.0
    messages = ['262702', '262704', '262714', '262715', '262721', '262724', '262726', '262824', '262846', '262848', '262850', '262852', '262882', '262900', '262901', '262904', '262906', '262909', '313551', '313579', '313645', '313654', '313655', '313674', '313680', '313681', '313683', '313689', '313690', '313693', '313707', '313870', '313872', '313873', '313874', '313878', '313880', '313881', '313882', '313895', '313897', '313901', '313902', '313904', '313907', '350164', '350172', '350173', '350190', '377774', '377921', '377924', '377925', '377926', '377971', '377972', '377996', '377999', '378005', '378060', '378127', '378138', '378198', '378199', '378227', '378295', '393148']
    nosy_count = 6.0
    nosy_names = ['tim.peters', 'rhettinger', 'christian.heimes', 'serhiy.storchaka', 'robert_smallshire', 'Austin Bingham']
    pr_nums = ['6121', '22584']
    priority = None
    resolution = 'remind'
    stage = 'resolved'
    status = 'closed'
    superseder = None
    type = 'enhancement'
    url = 'https://bugs.python.org/issue26680'
    versions = ['Python 3.11']

    @rob-smallshire
    Copy link
    Mannequin Author

    rob-smallshire mannequin commented Mar 31, 2016

    When the useful float.is_integer method was added the opportunity was missed to incorporate this method into the numeric tower defined in numbers.py. This increased the API distance between different number types, making them less substitutable than previously, leading to what might be considered to be absurd behaviour:

      >>> a = 5.0
      >>> b = 5
      >>> a.is_integer()
      True
      >>> b.is_integer()
      Traceback (most recent call last):
        File "<stdin>", line 1, in <module>
      AttributeError: 'int' object has no attribute 'is_integer'

    The first attached patch modifies Python to:

    1. Implement int.is_integer() to always return True
    2. Add Real.is_integer() as an abstract method in numbers.py
    3. Provide a default implementation in Rational.is_integer() in numbers.py
    4. Adds tests for is_integer() for int and Fraction.
    5. Documentation changes commensurate with above.

    Although the Decimal type deliberately lies outside the numeric tower for reasons not relevant here, the principle of least surprise suggests that it too should support is_integer(). In fact, the implementation already contains just such a function, although it is not exposed to Python. The second patch implements is_integer() for both the pure Python and C implementations of Decimal, again with commensurate tests and documentation changes.

    I hope these changes can be implemented to reduce the degree of surprise encountered when working with different number types in Python.

    @rob-smallshire rob-smallshire mannequin added type-feature A feature request or enhancement stdlib Python modules in the Lib dir labels Mar 31, 2016
    @rob-smallshire
    Copy link
    Mannequin Author

    rob-smallshire mannequin commented Mar 31, 2016

    Adding the second patch file.

    @SilentGhost SilentGhost mannequin added the extension-modules C modules in the Modules dir label Mar 31, 2016
    @rhettinger
    Copy link
    Contributor

    -0

    I question whether we ever needed a short-cut for x==int(x). Adding this to the numeric tower would cause it to propagate broadly including the int type. To me, this seems like feature creep resulting in language bloat.

    The decimal module has been around for a long time and no one has ever requested the feature. This suggests it would be just another unused method in a module that already has learnability and usability problems due to a fat API.

    @rhettinger
    Copy link
    Contributor

    One other thought: the name is_integer() is inconsistent with the nomenclature in numbers.py. Had this been included at the outset, its name would have been is_integral().

    @serhiy-storchaka
    Copy link
    Member

    Agree with Raymond.

    float.is_integer(x) is more efficient than x==int(x), but is this method used anywhere at all? It was added as a part of bpo-2224.

    @skrah
    Copy link
    Mannequin

    skrah mannequin commented Apr 1, 2016

    is_integer() is very important for writing new functions. libmpdec has
    it and it's used a lot inside mpdecimal.c.

    In this case though I assume Robert needs it for duck typing.

    @rob-smallshire
    Copy link
    Mannequin Author

    rob-smallshire mannequin commented Apr 1, 2016

    As for whether the shortcut float.is_integer(x) was needed, it has different behavior to x==int(x) when x is either NaN or an infinity. We must even deal with two different exception types OverflowError or ValueError respectively for these two values on conversion to int. That float.is_integer() simply returns False for these values makes it more straightforward to use robustly. The same would go for Decimal, which has the same behavior with respect to NaNs and infinities as float.

    I agree that is_integral may have been a better name, although is_integer has the advantage that it avoids conflating numeric values with either of the types 'int' or 'Integral'.

    The motivation for my patches is to converge the interfaces of the various number types so that we can simply, and robustly, check for integer values (as opposed to integer types) without needing to be concerned about the concrete number type, so long as it is Real. Indeed, this is largely the point of having a numeric tower at all. I am more motivated by usability and concision and correctness than efficiency concerns: I believe that where
    possible we should allow one number type to be substituted for another, and in particular int for any other Real type where purely mathematical - rather than representational operations - are in play.

    Use of the existing float.is_integer is compromised by the fact that people have an entirely reasonably habit of passing integers (particularly literals) to functions which accept floats which then fail if they use float.is_integer.

    Adding this method would reduce the educational load as the various number types would be more similar, not less.

    I work in industrial fields where computational geometry, and hence rationals, floats, infinities and large integers are a day-to-day occurrence. Ultimately, I care more about consistency within the numeric tower types (Real, float, int, Rational, Integral, Fraction) than I do about Decimal, which is why I separated my changes to Decimal into a separate patch.

    @skrah
    Copy link
    Mannequin

    skrah mannequin commented Apr 3, 2016

    I've been thinking about this, and I'm +1 for the change now.

    These structural typing issues for numbers come up regularly
    (see also msg257088), and the functions are so simple and
    self-explanatory that API-complexity does not really increase.

    In general, I understand the argument that Python has become
    too complex in some areas -- recently I had to go back to
    Python-2.7 in order to understand a certain detail about
    .pyc files (in 2.7 it was immediately obvious).

    But here I see on such problems.

    @rhettinger
    Copy link
    Contributor

    the functions are so simple and self-explanatory
    that API-complexity does not really increase.

    It increases complexity because it will show-up everywhere including places where it makes little sense.

    One place is int objects where its meaning and purpose will seem arcane to most Python programmers. For int objects, this is just total waste.

    With the fractions module, the method is also unnecessary because integral fractions all have a denominator of 1.

    With the decimal module, we were careful not to grow the API beyond what was in the spec or what was necessary to integrate it into Python (i.e. the usual magic methods). I think that was a good decision and would like to keep it that way.

    In general, the need for this method is almost nil (it would be a very, very rare Python programmer who would ever need this, and those that might what it are perfectly capable of writing a short function to handle their own requirements). In the OPs case, the motivation isn't inability to determine whether something is integral, it is more a desire for the test to be polymorphic with other types where the methods do not add any real value.

    The OPs notion of "absurd" behavior implies a rule that all float methods should be available for ints. That would suggest the is_integer, hex, fromhex, and as_integer_ratio would all need to propagate to the other types as well. I don't think we should start sliding down that slope.

    Another thought is that future updates to the decimal spec could make conflicting choices about what Decimal.is_integral() would return for Decimal('Infinity'). There could be a case to be made for true, for false, for NaN, or for setting one or more of the signal flags or traps.

    I'm glad that the OP separated out the request for Decimal given that his actual use cases involve everything except Decimal. The decimal class is intentionally not registered a Real (only as a Number) because it isn't interoperable with binary floats; hence, there has been no need to copy all the float methods into Decimal.

    AFAICT, this comes down to whether to push a float method into other types where we otherwise wouldn't do it, just to save the OP from one-line function:

    is_integral = lambda x:  isinstance(x, int) or isinstance(x, Fraction) and x.denominator == 1 or isinstance(x, float) and x.is_integer()

    @rhettinger
    Copy link
    Contributor

    FWIW, I think the reasoning in http://bugs.python.org/issue1093 applies here as well. The need is insufficient to warrant inclusion in the numeric tower and propagation to types like int and Fraction.

    @rob-smallshire
    Copy link
    Mannequin Author

    rob-smallshire mannequin commented Apr 4, 2016

    To be clear, I'm not arguing that is_integer is in the same category as hex and fromhex; is_integer is a mathematical property of the number, whereas hex and from hex are representational. Nobody expects interoperability of string representations of the different number types.

    Neither do I take issue with the general argument against enlarging the API. In fact, if float.is_integer did not exist, I would not be campaigning for it to be invented.

    What I do take issue with is already having a method on float which makes sense for all Real numbers, but then not supporting it for those other Real types. This just gets in the way of programming generally in terms of *numbers* rather than concrete types, especially when the is_integer method is being advocated as the *right way* to do such a test. This is can lead to other problems as people unthinkingly convert other number types to floats, with loss of information, just to get access to this convenience method.

    I'm (really) surprised that structural typing and polymorphism over numbers carries so little weight – a cursory look at StackOverflow* shows that there are already too many people being encouraged to answer the 'is integral' question with isinstance(x, int) when that is not what they mean or need.

    As a precedent we already have int.numerator, int.denominator, int.real and int.imag which are presumably using Raymond's argument are also 'total waste', but the reality is that the presence of these attributes causes no impediment to learning and makes many tasks easier with fewer special cases. When working with rationals, I frequently rely on the fact that ints implement the Rational interface. When working with complex numbers, I frequently rely on the fact that both int and float implement the Complex interface. For example. I have used use these attributes in the constructor for a fixed-point number type, and was thankful for their existence.

    I'm also happy to set the Decimal aspect of my proposal to one side as Decimal is explicitly outside the numeric tower.

    This isn't about me avoiding writing trivial one-line functions or "the OPs use case". I'm trying to help you make Python more predictable and easier to use. I'm far from the first person to be surprised by this.

    I'd be happy to trade adding is_integer to the numeric tower for a deprecation notice on float.is_integer - the outcome is the same - polymorphic number types with fewer special cases.

    <http://stackoverflow.com/questions/17170226/is-integer-not-working\>
    <http://stackoverflow.com/questions/6239967/determining-whether-an-value-is-a-whole-number-in-python/6239987#6239987\>
    <http://stackoverflow.com/questions/33002747/try-except-float-but-not-integer/33002796#33002796\>
    <http://stackoverflow.com/a/22053804/107907\>
    <http://stackoverflow.com/questions/36209324/trouble-using-is-integer-in-python\>

    @skrah
    Copy link
    Mannequin

    skrah mannequin commented Apr 4, 2016

    I agree that Robert's "absurdity" argument was unfortunate and could
    be reversed: Many people would consider an (10).is_integer() method
    absurd.

    I'm also only moderately interested in OOP or classification in general, but we *do* have a numeric tower modeled after Scheme,
    so here goes:

    scheme@(guile-user)> (integer? 487)
    $1 = #t
    scheme@(guile-user)> (integer? 1.2)
    $2 = #f
    scheme@(guile-user)> (integer? 1.0)
    $3 = #t
    scheme@(guile-user)> (integer? 1/7)
    $4 = #f
    scheme@(guile-user)> (integer? 100/10)
    $5 = #t
    scheme@(guile-user)>

    The ACL2 theorem prover has the same:

    ACL2 !>(integerp 100)
    T
    ACL2 !>(integerp 100/10)
    T
    ACL2 !>(integerp 100/7)
    NIL

    For me, these functions are something fundamental. I'd prefer
    them to be exposed in a functional manner like above, but we
    do have the numeric tower.

    @rhettinger
    Copy link
    Contributor

    • For most users, this will just be noise (even the existing float method is rarely used). It is unimportant enough that Python existed without it for a very long time and it is unimportant enough that it didn't arise during the lengthy process of creating the decimal module.

    • The numeric tower doesn't require that we take new methods and push them to every type whether or not it makes sense. Most of the ABCs have only a subset of the methods in the concrete types.

    • There are already simple workarounds using a try/except or a conditional expression.

    -1 I really don't want more clutter added to all the numeric classes. (Clutter being something rarely needed, easily implemented in other ways, something that looks weird or confusing in classes like int or Fraction, something that we have done without to 26 years, something not covered by the decimal spec, and something that isn't part of the floats API for either Java* or Smalltalk)

    @rob-smallshire
    Copy link
    Mannequin Author

    rob-smallshire mannequin commented Apr 5, 2016

    Java makes no claim to have a numeric tower. Amongst the dynamic languages I surveyed Matlab (isinteger), Javascript ES6 (isInteger), PHP (is_integer), R (is.integer), TCL (is entier), and as we have seen Scheme (integer?) all have methods for testing for integer values. Python has a numeric tower modelled on Scheme. In the Scheme documentation we find this:

    "...the integer 5 may have several representations. Scheme's numerical operations treat number objects as abstract data, as independent of their representation as possible. Although an implementation of Scheme may use many different representations for numbers, this should not be apparent to a casual programmer writing simple programs."

    This is what I'm advocating.

    There isn't a single mathematical (as opposed to representational) method on int that isn't 'inherited' from the numeric tower. There are exactly two methods on float which aren't inherited from the tower: is_integer and as_integer_ratio. So I think it's would be a stretch to claim that "Most of the [numerical] ABCs have only a subset of the [numerical] methods in the [numerical] concrete types."

    Rather than looking at the numeric tower as a construct which forces proliferation of methods, it would be better to look on it as a device to prevent bloat. I risk straying off topic here, but I want to give an example of why the numeric tower is important:

    Were float to inherit from Rational, rather than Real (all finite floats are rationals with a power-of-two denominator, all Decimals are rationals with a power-of-ten denominator, so this is reasonable) then the as_integer_ratio method which was added to float and latterly Decimal (http://bugs.python.org/issue25928), arguably cluttering their interfaces, may have been deemed unnecessary. The numerator and denominator attributes present in Rational could have been used instead. I think this is an example of lack of adherence to the numeric tower (at least in spirit in the case of Decimal) resulting in interface clutter or bloat.

    The consequent control-flow complexity required handle numeric objects as 'abstract data' is surprising: statistics._exact_ratio is a good example of this. I count five API tests just to be able to treat numbers as, well, just numbers.

    @skrah
    Copy link
    Mannequin

    skrah mannequin commented Apr 5, 2016

    On Tue, Apr 05, 2016 at 01:10:25PM +0000, Robert Smallshire wrote:

    Were float to inherit from Rational, rather than Real ...

    This would break the Liskov substitution principle.

    @rob-smallshire
    Copy link
    Mannequin Author

    rob-smallshire mannequin commented Apr 5, 2016

    > Were float to inherit from Rational, rather than Real ...

    This would break the Liskov substitution principle.

    How so? Rational extends Real with only numerator, denominator and __float__. Isn't the existence of float.as_integer_ratio demonstration that numerator and denominator could be implemented?

    @skrah
    Copy link
    Mannequin

    skrah mannequin commented Apr 5, 2016

    On Tue, Apr 05, 2016 at 02:20:19PM +0000, Robert Smallshire wrote:

    >> Were float to inherit from Rational, rather than Real ...

    > This would break the Liskov substitution principle.

    How so? Rational extends Real with only numerator, denominator and __float__. Isn't the existence of float.as_integer_ratio demonstration that numerator and denominator could be implemented?

    Substitution principle:

    Let phi(x) be a property provable about objects x of type T. Then phi(y) should
    be true for objects y of type S where S is a subtype of T.

    Use:

    Let phi(n) = forall n: n elt nat => (1 / n) * n == 1

    Counterexample:

    n == 9992

    @rob-smallshire
    Copy link
    Mannequin Author

    rob-smallshire mannequin commented Apr 5, 2016

    Thanks Stefan for the illuminating example.

    I knew I shouldn't have strayed off-topic.

    @rob-smallshire
    Copy link
    Mannequin Author

    rob-smallshire mannequin commented Mar 10, 2018

    I've recently run into this issue impeding duck-typing between int and float again, when used in conjunction the int.__pow__, which may variously return an int or float depending on the value - not the type - of the arguments.

    This is succinctly demonstrated by this example:

      >>> (10 ** -2).is_integer()
      False
      >>> (10 ** 2).is_integer()
      Traceback (most recent call last):
        File "<stdin>", line 1, in <module>
      AttributeError: 'int' object has no attribute 'is_integer'

    I hear the argument about Python being harder to learn if more methods are supported on the built-in types - and perhaps float.is_integer should never have been added, but now its there, but I think Python is harder to learn and teach in the presence of these differences. Is is harder to learn "Real numbers support an is_integer() method", than it is "float supports an is_integer() method"?

    I'm happy to put in the work bring my original patches up-to-date, or create a PR depending on what current process is.

    @rhettinger
    Copy link
    Contributor

    Sorry Robert, but I object to this going forward.

    1. We do not and should not require that every float() method also be in int():
       >>> set(dir(float)) - set(dir(int))			 
       {'fromhex', 'hex', 'is_integer', '__getformat__', '__setformat__', 
        'as_integer_ratio'}
    1. Your use case is trivially solved in a portable, trivial, and readable way:

      a == int(a)

    2. I really don't want to clutter the other types with this method when it does nothing useful for those types. In particular, I expect that the presence of "is_integer()" in the int() class will likely create more confusion than it solves (perhaps not for you, but for the vast majority of users, none of whom have ever requested this behavior over the entire history of the language).

    3. Also, I don't what this to have to propagate to every project that ever registers their custom numeric types with the numeric tower. Adding this method to the tower is essentially making a requirement that everyone, everywhere must add this method. That is not in the spirit of what the ABCs are all about -- they mostly require a small and useful subset of the behaviors of the related concrete classes (i.e. the concrete collections all have more methods than are required by their collections.abc counterparts).

    4. Lastly, the spirit of the decimal module was to stick as closely as possible to the decimal specification and assiduously avoid extending the spec with new inventions (risking duplication of functionality, risking non-portability with other implementations, risking not handling special values in a way that is consistent with the spec, risking our going down a path that intentionally not chosen by the spec creators, or risking being at odds with subsequent updates to the spec).

    @mdickinson
    Copy link
    Member

    One quibble with Raymond's response:

    1. Your use case is trivially solved in a portable, trivial, and readable > way:

      a == int(a)

    For Decimal, I'd recommend using a == a.to_integral_value() instead. Using a == int(a) will be inefficient if a has large exponent, so it's not a good general-purpose solution (though it's probably good enough in most real-world cases).

    Here's an extreme example:

    In [1]: import decimal
    In [2]: x = decimal.Decimal('1e99999')
    In [3]: %timeit x == int(x)
    1.42 s ± 6.27 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
    In [4]: %timeit x == x.to_integral_value()
    230 ns ± 2.03 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
    

    @rob-smallshire
    Copy link
    Mannequin Author

    rob-smallshire mannequin commented Mar 12, 2018

    To respond to Raymond's points:

    1. Nobody is suggesting that every float method should also be available on
      int. Clearly some methods on float are not applicable to int.

    2. Taken narrowly, you're right that is_integer() does nothing useful for
      int. Neither do imag, denominator, __floor__, or __trunc__. They're all
      present so we can successfully use integers in duck-typing situations along
      with the other number types. My claim is that int.is_integer() is useful
      for the same reasons.

    The problem isn't that you or I don't know that we should write a == int(a)
    to be portable, the problem is that code that *already* uses x.is_integer()
    fails hard when x happens to be an int. As I've demonstrated, some built-in
    operators and functions can return either int or float, even when the
    operand types are all int, depending only on the *values* of the operands.

    This wouldn't matter if nobody ever wrote f.is_integer(), and instead used
    the trivially portable solution, but they do, and at your behest: In 2011
    you published "f.is_integer() is the new way to test whether a float is
    integral. The old way, x==int(x), is history." The reality is that folks
    often write Python functions which accept *numbers*, without giving too
    much thought to whether calling my_func(42.0) will work, but my_func(42)
    will cause an unhandled exception that nobody is expecting. Indeed, one of
    the joys of Python is that we often don't need to overthink this.

    The trivial portable solution is also nearly three times slower than
    float.is_integer() and int.is_integer(). Moreso if you package it up in a
    function so it can be applied to more complex expressions in, say, a
    comprehension, where an intermediate assignment is not possible.

    I'm not the only person to be thrown by this. See this:

    is_integer() not working: https://stackoverflow.com/
    

    questions/17170226/is-integer-not-working/17170511

    and this:

    https://www.reddit.com/r/learnpython/comments/4tp4hy/
    need_help_with_identify_number_as_integer/

    and this

    https://wiesmann.codiferes.net/wordpress/?p=13366
    

    Furthermore, once the is_integer() method is discovered, it leads to folks
    writing odd code in order to leverage it, such as always converting user
    integer input to float in order to check that it's really is an integer
    (which of course subtly limits the precision of allowable integers).
    There's an example of this on page 14 of the book *Doing Math With Python*.
    https://www.amazon.com/Doing-Math-Python-Programming-Statistics/dp/1593276400

    Other prolific and widely respected Python programmers have described this
    behaviour as "kind of nuts" (though I'm not going to involve them here).
    The behaviour has also invited unfortunate comparisons with similar
    non-obvious behaviour in Javascript.

    1. I'd be very surprised if the presence of this method on int caused any
      more confusion, or impediment to learning than do the presence of int.imag
      or int.denominator.

    2. I'm less bothered about the numeric tower aspect than I am about
      duck-type compatibility between the built-in types int and float. That
      said, a key part of what I think is your concern about creating work for
      subclass implementers can be addressed by providing a default
      implementation Real.is_integer() in terms of int(x) == x.

    3. The decimal spec doesn't require an is_decimal function, but it doesn't
      forbid it either. In any case, the Decimal implementation already
      implements is_integer internally as cpx_mpd_isinteger – and uses it a
      great deal, which demonstrates its utility. My patch simply exposes it to
      Python. There's no danger of violating any specification, unless that
      specification says that you must not implement a method called is_integer,
      which it doesn't, especially as we would be using a definition which is
      already de facto compatible with the standard. I don't care very much
      about Decimal either for my own work, especially as it already stands apart
      from the numeric tower. I implemented it to be consistent with my argument
      about duck typed numbers (which still largely holds for Decimal, except for
      floor division and modulus I believe).

    Solutions for which use a == int(a) or a == a.to_integral_value() fail
    for NaN and infinities, whereas float.is_integer() is more robust. It turns
    out the trivial portable solution isn't so trivial, or so portable, if
    implemented robustly, performantly and with duck-typing in mind.

    Ultimately, my argument is one about duck typing across numbers types. If
    if that abstraction isn't valued, I have nowhere to go.

    *Robert Smallshire | *Managing Director
    *Sixty North* | Applications | Consulting | Training
    rob@sixty-north.com | T +47 63 01 04 44 | M +47 924 30 350
    http://sixty-north.com

    On 11 March 2018 at 05:58, Raymond Hettinger <report@bugs.python.org> wrote:

    Raymond Hettinger <raymond.hettinger@gmail.com> added the comment:

    Sorry Robert, but I object to this going forward.

    1. We do not and should not require that every float() method also be in
      int():

      >>> set(dir(float)) - set(dir(int))
      {'fromhex', 'hex', 'is_integer', '__getformat__', '__setformat__',
      'as_integer_ratio'}

    2. Your use case is trivially solved in a portable, trivial, and readable
      way:

      a == int(a)

    3. I really don't want to clutter the other types with this method when it
      does nothing useful for those types. In particular, I expect that the
      presence of "is_integer()" in the int() class will likely create more
      confusion than it solves (perhaps not for you, but for the vast majority of
      users, none of whom have ever requested this behavior over the entire
      history of the language).

    4. Also, I don't what this to have to propagate to every project that ever
      registers their custom numeric types with the numeric tower. Adding this
      method to the tower is essentially making a requirement that everyone,
      everywhere must add this method. That is not in the spirit of what the
      ABCs are all about -- they mostly require a small and useful subset of the
      behaviors of the related concrete classes (i.e. the concrete collections
      all have more methods than are required by their collections.abc
      counterparts).

    5. Lastly, the spirit of the decimal module was to stick as closely as
      possible to the decimal specification and assiduously avoid extending the
      spec with new inventions (risking duplication of functionality, risking
      non-portability with other implementations, risking not handling special
      values in a way that is consistent with the spec, risking our going down a
      path that intentionally not chosen by the spec creators, or risking being
      at odds with subsequent updates to the spec).

    ----------


    Python tracker <report@bugs.python.org>
    <https://bugs.python.org/issue26680\>


    @rob-smallshire
    Copy link
    Mannequin Author

    rob-smallshire mannequin commented Mar 12, 2018

    Apologies for the email splurge. That's the first and last time I'll use the email interface to bugs.python.org.

    @serhiy-storchaka
    Copy link
    Member

    float.is_integer() was added 6f34109 (unfortunately no issue number for looking at the discussion preceded it). I don't know reasons. The same changeset added implementations of complex.is_finite(), int.is_finite(), long.is_finite(), float.is_inf() and float.is_nan(), but they were disabled by default (they are still a dead code in sources). The same changeset added well known functions math.isinf(), math.isnan(), cmath.isinf() and cmath.isnan().

    @rhettinger rhettinger added the 3.8 only security fixes label Mar 12, 2018
    @mdickinson
    Copy link
    Member

    @serhiy-storchaka
    Copy link
    Member

    What is the use case of float.is_integer() at all? I haven't found its usages in large projects on GitHub. Only in playing examples where it is (mis)used in cases like (x/5).is_integer() (x % 5 == 0 is better) or (x**0.5).is_integer() (wrong for some floats or large integers).

    Maybe it should be removed in Python 3.0.

    @rhettinger
    Copy link
    Contributor

    Thanks. I'm hoping that this will easy now the most of the details have already been worked out.

    @maxusz666 maxusz666 mannequin added build The build process and cross-build stdlib Python modules in the Lib dir docs Documentation in the Doc dir topic-2to3 topic-ctypes topic-asyncio topic-argument-clinic topic-C-API and removed type-feature A feature request or enhancement labels Mar 22, 2021
    @rhettinger rhettinger added type-feature A feature request or enhancement and removed build The build process and cross-build stdlib Python modules in the Lib dir docs Documentation in the Doc dir extension-modules C modules in the Modules dir topic-2to3 topic-ctypes topic-asyncio topic-argument-clinic topic-C-API labels Mar 22, 2021
    @rhettinger rhettinger added 3.11 only security fixes and removed 3.10 only security fixes labels May 4, 2021
    @rhettinger
    Copy link
    Contributor

    This has gone stale and I've been unable to contact the OP. Marking as closed for now. Please reopen if this comes back to life again and I'll review the PR.

    @ezio-melotti ezio-melotti transferred this issue from another repository Apr 10, 2022
    Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
    Labels
    3.11 only security fixes stdlib Python modules in the Lib dir type-feature A feature request or enhancement
    Projects
    None yet
    Development

    No branches or pull requests

    6 participants