I have some functions doing math stuff which needs to take integer agrmuents.
I know that I can force using int by using condition isinstance(x, int)
or more strict type(x) == int, but IMO it isn't pythonic.
I think my Python code shouldn't reject 2.0 just because it's float.
What's the best way to check if value is logically integer?
By logically integer I mean value of any type, which represents integer.
It should be able to be used in arithmetic operations like int, but I don't have to check it,
because I belive that in Python any set of conditions can get fooled.
For example,
True, -2, 2.0, Decimal(2), Fraction(4, 2) are logically integers,
when '2' and 2.5 are not.
At the moment I use int(x) == x, but I'm not sure if it's the best solution.
I know I can use float(x).is_integer().
I also saw x % 1 == 0.
Normally one would check against the Integral ABC (Abstract Base Class), but floating point values are not normally meant to be treated as integers no matter their value. If you want that, take note of their is_integer property:
(1324.34).is_integer()
#>>> False
(1324.00).is_integer()
#>>> True
and the code is then just:
from numbers import Integral
def is_sort_of_integer(value):
if isinstance(value, Integral):
return True
try:
return value.is_integer()
except AttributeError:
return False
If you also want to deal with Decimals, Fractions and so on, which don't have an is_integer method, the best option would probably be just:
from numbers import Number, Integral
def is_sort_of_integer(value):
if isinstance(value, Integral):
return True
if isinstance(value, Number):
try:
return not value % 1
except TypeError:
return False
return False
The Integral check shouldn't be needed in this case, but it's probably best to keep it.
One way to solve this is by using a metaclass to define your custom implementation of __instancecheck__, then define a concrete class having the metaclass, and use isinstance with your concrete class.
This has the downside of pulling in metaclass machinery, which is often extraneous.
But it has the upside of cleanly encapsulating whatever properties you desire to use for what you mean by "logically integer" for your application.
Here's some code to show this approach:
class Integral(type):
def __instancecheck__(self, other):
try:
cond1 = int(other) == other
cond2 = (other >= 1.0) or (other < 1.0)
# ... plus whatever other properties you want to check
return all([cond1, cond2,])
except:
return False
class IntLike:
__metaclass__ = Integral
print isinstance(-1, IntLike)
print isinstance('1', IntLike)
print isinstance(27.2, IntLike)
print isinstance(27.0, IntLike)
print isinstance(fractions.Decimal(2), IntLike)
print isinstance(fractions.Fraction(4, 2), IntLike)
It prints:
True
False
False
True
True
True
Note that it is important to get rid of the idea that the mathematical concept of being logically integer should apply to your program. Unless you bring in some proof-checking machinery, you won't get that. For example, you mention properties like certain functions being available, and specifically sqrt -- but this won't be available for negative integers unless you implement custom behavior to check for complex results.
It will be application-specific. For example, someone else might pick up this code and modify it so that '1' does register as IntLike, and perhaps for the sake of their application it will be correct.
This is why I like the metaclass approach here. It lets you explicitly denote each condition that you are imposing, and store them in one location. Then using the regular isinstance machinery makes it very clear to code readers what you are trying to do.
Lastly, note that no given conditions will always be perfect. For example, the class below could be used to 'fool' the int(x) == x trick:
class MyInt(object):
def __init__(self, value):
self.value = value
def __int__(self):
return int(self.value)
def __add__(self, other):
return self.value + other
#... define all needed int operators
def __eq__(self, other):
if isinstance(other, float):
raise TypeError('How dare you compare me to a float!')
return self.value == other
# ..etc
Then you get behavior like this:
In [90]: mi = MyInt(3)
In [91]: mi + 4
Out[91]: 7
In [92]: mi == 3
Out[92]: True
In [93]: int(mi) == mi
Out[93]: True
In [94]: mi == 3.0
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-93-827bea4a197f> in <module>()
----> 1 mi == 3.0
<ipython-input-89-66fec92fab7d> in __eq__(self, other)
13 def __eq__(self, other):
14 if isinstance(other, float):
---> 15 raise TypeError('How dare you compare me to a float!')
16 return self.value == other
17
TypeError: How dare you compare me to a float!
and whether or not isinstance(mi, IntLike) returns True will be totally dependent on how you have implemented the comparison operators for MyInt and also whatever extra checks you have made in Integral's __instancecheck__.
There are some cases, which none of int(x) == x, x.isinteger() & x % 1 == 0 can handle the way I would like to.
Example:
>>> big_float = 9999999999999999.1
The big_float is big enough to ignore substracting some small number from it (AFAIK, it's called underflow):
>>> big_float -1 == big_float
True
Then
>>> def fib(n):
... current, prev = 1, 0
... while n > 0:
... current, prev, n = current+prev, current, n-1
... return prev
...
>>> fib(big_float) #unwanted infinite loop
There are some cases, which none of int(x) == x, x.isinteger() & x % 1 == 0 can handle the way I would like to.
>>> int(big_float) == big_float
True
>>> big_float.is_integer()
True
>>> big_float % 1 == 0
True
Solution
We can check if big_float in the same way as int(big_float):
>>> int(big_float) -1 == big_float -1
False
Of course, this method works also for more trivial cases, like this:
>>> x = 2.1
>>> int(x) -1 == x -1
False
Of course, you don't have to substract 1, you can use whatever mathematical operation you need.
Note that this condition may throw exception:
>>> x = '2'
>>> int(x) -1 == x -1
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unsupported operand type(s) for -: 'str' and 'int'
Simple, try to convert it to an integer. If int() works, it is "logically an integer", otherwise it is not.
try:
int(thing)
is_integer = True
except ValueError:
is_integer = False
But, typically, rather than do it like this you would just use int(thing) in the code you needed this for, and just catch the error if it ends up not being an integer and handle that case appropriately.
Related
My working environment:
OS: Ubuntu 18.04 (64 bits)
Python version: 3.8.0 (64 bits)
I've a question about the unittest documentation, more precisely, the difference between assertTrue and assertIs methods. Here is what the online documentation says:
https://docs.python.org/3/library/unittest.html
assertTrue(expr, msg=None)
assertFalse(expr, msg=None)
Test that expr is true (or false).
Note that this is equivalent to bool(expr) is True and not to expr is
True (use assertIs(expr, True) for the latter).
I don't really understand the difference, that is, in the case where I have a boolean type with a True value, then why should I use assertIs instead of assertTrue? Given that bool(True) is True also returns the True boolean value I don't see the difference and impact on tests. I'd appreciate if you could kindly make some clarification.
To answer the question you have to understand the difference between is and == comparison operators, imagine you have two different boys, but they are both Peters, they have the same name so == will give you true, but is will give you False, because they are not one person.
import unittest
class MyClass:
def __init__(self, value):
self.value = value
def __eq__(self, other):
return self.value == other.value
class TestStringMethods(unittest.TestCase):
def test_your_func(self):
x = MyClass(5)
y = MyClass(5)
z = x
print(x is y) # False x is not y, they are separate object
print(x == y) # True 5 == 5
print(x is z) # True, because z is the reference to the same object
print(z == y) # True 5 == 5
# self.assertIs(x, y) # if you uncomment the line you'll get error
self.assertIs(x, z)
if __name__ == '__main__':
unittest.main()
As per AssertTruem it has absolutely different purpose to use, e.g. if []: is False and if [1] is True.
You are basically right - in most cases in tests you will probably use assertTrue for the check instead of assertIs(True).
The only really useful case where you actually need assertIs(True) would be if you want to check that the checked expression actually returns a bool value, e.g.:
def test_bool(self):
a = True
b = 1
self.assertTrue(a)
self.assertIs(a, True)
self.assertTrue(b)
self.assertIsNot(b, True)
This could be interesting if you have a function that may return values of different types, and you want to ensure that it returns a concrete bool value. Consider a (nonsense) function:
def fct(param):
if param < 0:
return False
if param > 0:
return True
return 42
...
def test_fct(self):
assertTrue(fct(1)) # will not ensure that it returns True
assertTrue(fct(0)) # also passes
assertIs(True, fct(1)) # passes as intended
assertIs(True, fct(0)) # fails, as it should
It can also help to ensure that your tested function that is supposed to return a bool value actually does that (and not just some value that would evaluate to True or False, like None or a number, due to some bug).
This all works of course because (like None), True and False are singleton objects, e.g. there is always only one of each of them.
Note: while the accepted answer achieves the result I wanted, and #ecatmur answer provides a more comprehensive option, I feel it's very important to emphasize that my use case is a bad idea in the first place. This is explained very well in #Jason Orendorff answer below.
Note: this question is not a duplicate of the question about sys.maxint. It has nothing to do with sys.maxint; even in python 2 where sys.maxint is available, it does NOT represent largest integer (see the accepted answer).
I need to create an integer that's larger than any other integer, meaning an int object which returns True when compared to any other int object using >. Use case: library function expects an integer, and the only easy way to force a certain behavior is to pass a very large integer.
In python 2, I can use sys.maxint (edit: I was wrong). In python 3, math.inf is the closest equivalent, but I can't convert it to int.
Since python integers are unbounded, you have to do this with a custom class:
import functools
#functools.total_ordering
class NeverSmaller(object):
def __le__(self, other):
return False
class ReallyMaxInt(NeverSmaller, int):
def __repr__(self):
return 'ReallyMaxInt()'
Here I've used a mix-in class NeverSmaller rather than direct decoration of ReallyMaxInt, because on Python 3 the action of functools.total_ordering would have been prevented by existing ordering methods inherited from int.
Usage demo:
>>> N = ReallyMaxInt()
>>> N > sys.maxsize
True
>>> isinstance(N, int)
True
>>> sorted([1, N, 0, 9999, sys.maxsize])
[0, 1, 9999, 9223372036854775807, ReallyMaxInt()]
Note that in python2, sys.maxint + 1 is bigger than sys.maxint, so you can't rely on that.
Disclaimer: This is an integer in the OO sense, it is not an integer in the mathematical sense. Consequently, arithmetic operations inherited from the parent class int may not behave sensibly. If this causes any issues for your intended use case, then they can be disabled by implementing __add__ and friends to just error out.
Konsta Vesterinen's infinity.Infinity would work (pypi), except that it doesn't inherit from int, but you can subclass it:
from infinity import Infinity
class IntInfinity(Infinity, int):
pass
assert isinstance(IntInfinity(), int)
assert IntInfinity() > 1e100
Another package that implements "infinity" values is Extremes, which was salvaged from the rejected PEP 326; again, you'd need to subclass from extremes.Max and int.
Use case: library function expects an integer, and the only easy way to force a certain behavior is to pass a very large integer.
This sounds like a flaw in the library that should be fixed in its interface. Then all its users would benefit. What library is it?
Creating a magical int subclass with overridden comparison operators might work for you. It's brittle, though; you never know what the library is going to do with that object. Suppose it converts it to a string. What should happen? And data is naturally used in different ways as a library evolves; you may update the library one day to find that your trick doesn't work anymore.
It seems to me that this would be fundamentally impossible. Let's say you write a function that returns this RBI ("really big int"). If the computer is capable of storing it, then someone else could write a function that returns the same value. Is your RBI greater than itself?
Perhaps you can achieve the desired result with something like #wim's answer: Create an object that overrides the comparison operators to make "<" always return false and ">" always return true. (I haven't written a lot of Python. In most object-oriented languages, this would only work if the comparison puts your value first, IF RBI>x. If someone writes the comparison the other way, IF x>RBI, it will fail because the compiler doesn't know how to compare integers to a user-defined class.)
In Python 3.5, you can do:
import math
test = math.inf
And then:
test > 1
test > 10000
test > x
Will always be true. Unless of course, as pointed out, x is also infinity or "nan" ("not a number").
How can I represent an infinite number in Python?
Answered by #WilHall
You should not be inheriting from int unless you want both its interface and its implementation. (Its implementation is an automatically-widening set of bits representing a finite number. You clearly dont' want that.) Since you only want the interface, then inherit from the ABC Integral. Thanks to #ecatmur's answer, we can use infinity to deal with the nitty-gritty of infinity (including negation). Here is how we could combine infinity with the ABC Integral:
import pytest
from infinity import Infinity
from numbers import Integral
class IntegerInfinity(Infinity, Integral):
def __and__(self, other):
raise NotImplementedError
def __ceil__(self):
raise NotImplementedError
def __floor__(self):
raise NotImplementedError
def __int__(self):
raise NotImplementedError
def __invert__(self, other):
raise NotImplementedError
def __lshift__(self, other):
raise NotImplementedError
def __mod__(self, other):
raise NotImplementedError
def __or__(self, other):
raise NotImplementedError
def __rand__(self, other):
raise NotImplementedError
def __rlshift__(self, other):
raise NotImplementedError
def __rmod__(self, other):
raise NotImplementedError
def __ror__(self, other):
raise NotImplementedError
def __round__(self):
raise NotImplementedError
def __rrshift__(self, other):
raise NotImplementedError
def __rshift__(self, other):
raise NotImplementedError
def __rxor__(self, other):
raise NotImplementedError
def __trunc__(self):
raise NotImplementedError
def __xor__(self, other):
raise NotImplementedError
def test():
x = IntegerInfinity()
assert x > 2
assert not x < 3
assert x >= 5
assert not x <= -10
assert x == x
assert not x > x
assert not x < x
assert x >= x
assert x <= x
assert -x == -x
assert -x <= -x
assert -x <= x
assert -x < x
assert -x < -1000
assert not -x < -x
with pytest.raises(Exception):
int(x)
with pytest.raises(Exception):
x | x
with pytest.raises(Exception):
ceil(x)
This can be run with pytest to verify the required invariants.
Another way to do this (very much inspired by wim's answer) might be an object that isn't infinite, but increases on the fly as needed.
Here's what I have in mind:
from functools import wraps
class AlwaysBiggerDesc():
'''A data descriptor that always returns a value bigger than instance._compare'''
def __get__(self, instance, owner):
try:
return instance._compare + 1
except AttributeError:
return instance._val
def __set__(self, instance, value):
try:
del instance._compare
except AttributeError:
pass
instance._val = value
class BiggerThanYou(int):
'''A class that behaves like an integer but that increases as needed so as to be
bigger than "other" values. Defaults to 1 so that instances are considered
to be "truthy" for boolean comparisons.'''
val = AlwaysBiggerDesc()
def __getattribute__(self, name):
f = super().__getattribute__(name)
try:
intf = getattr(int,name)
except AttributeError:
intf = None
if f is intf:
#wraps(f)
def wrapper(*args):
try:
self._compare = args[1]
except IndexError:
self._compare = 0 # Note: 1 will be returned by val descriptor
new_bigger = BiggerThanYou()
try:
new_bigger.val = f(self.val, *args[1:])
except IndexError:
new_bigger.val = f(self.val)
return new_bigger
return wrapper
else:
return f
def __repr__(self):
return 'BiggerThanYou()'
def __str__(self):
return '1000...'
Something like this might avoid a lot of weird behavior that one might not expect. Note that with this kind of approach, if two BiggerThanYou instances are involved in an operation, the LHS would be considered bigger than the RHS.
EDIT: currently this is not working- I'll fix it later. it seems I am being bitten by the special method lookup functionality.
Basically I am writing a function that depends on a numerical input x, a number between 0 and 1. I want the default value of x to be, say, x=0.5. However, I also want to provide an option to the user that allows them to let the program select x for them using some algorithm. Is there an elegant way to handle that choice with one function argument?
I'm thinking something like this:
def foo(x=0.5):
if x == "pick for me":
return complicated_algorithm_that_picks_x()
else:
return x
def complicated_algorithm_that_picks_x():
print "Thinking hard..."
return 0.1234567
which would return:
>>> foo()
0.5
>>> foo(0.3)
0.3
>>> foo("pick for me")
Thinking hard...
0.1234567
But this looks really inelegant, since the user has to know what magic string to pass to invoke the selection algorithm. Any ideas how I can handle this more cleanly?
I was thinking having an additional Boolean argument called pick (that defaults to False), which when True will invoke the x picking function. But then users might pass both, say, x=0.3 and pass=True, in which case I have to arbitrarily ignore one of the choices. Looks clumsy again.
There are three things you might consider:
Split the one function into two.
Use a class.
Multiple default arguments.
Here there are, in no particular order:
Split one function into two
If you want to do two different things in one function and you're having trouble designing a natural interface, it might be a sign that the one function should become two:
def foo_picked_for_me():
x = pick_x()
return foo(x)
def foo(x):
# do foo
pass
I don't know how this strikes you, but it's simple, clear, and that means its often preferable.
Use a class
Default arguments are nice, but a function's interface can only get so complicated before it starts making more sense to handle option setting with a class:
class Foo:
def __init__(self):
self.x = 0.5
def pick_x_for_me(self):
self.x = pick_x()
def foo(self):
# do foo with self.x
As EOL suggests below, it's perfectly pythonic to leave x "exposed", and to allow the user to change it. You say, though, that x must be between 0 and 1, so it might make sense to do some bounds checking with the setter for x:
class Foo(object):
def __init__(self):
self._x = 0.5
#property
def x(self):
return self._x
#x.setter
def x(self, value):
if 0 <= value <= 1:
self._x = value
else:
raise ValueError("x must be between 0 and 1")
def pick_x_for_me(self):
self._x = pick_x()
def foo(self):
pass
# do foo with self._x
Multiple default arguments
The last option is analogous to what other posters have given: use two arguments, and throw an exception if the user does something contradictory. I'd consider allowing three forms of call:
# x gets its default value of 0.5
foo()
# x gets the specified value
foo(x=.42)
# x is picked for me
foo(pick_for_me=True)
Additionally, if I write:
foo(x=.42, pick_for_me=True)
I'll throw an exception. Some code that implements this follows:
def foo(x=None, pick_for_me=None):
if x is None and pick_for_me is None:
x = 0.5
elif pick_for_me and x:
raise RuntimeError("You can't set both!")
elif pick_for_me:
x = picking_algorithm()
# else x was set, so leave it be
This is kind of complicated, and I'm not so sure I like the API. Just make sure you document the behavior well enough so that the user knows how to use the thing.
Try this:
def foo(x=0.5, use_complex_algo=False):
if use_complex_algo == False:
return x
else:
return complicated_algorithm_that_picks_x()
#ouput
print foo() # returns: 0.5
print foo(0.3) # returns: 0.3
print foo(use_complex_algo=True) # returns: 0.1234567
I would probably define a constant in the module that indicates to generate a random value. For example:
# foo.py
import random
RANDOM = 'random'
def foo(x=0.5):
if x == RANDOM:
x = random.random()
if x < 0 or x > 1:
raise ValueError('x must be between 0 and 1')
return x
Then, to use it is pretty straightforward:
>>> import foo
>>> foo.foo(0.1)
0.1
>>> foo.foo()
0.5
>>> foo.foo(foo.RANDOM)
0.4388309758578337
>>> foo.foo(foo.RANDOM)
0.5351558099071574
>>> foo.foo(7)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "foo.py", line 9, in foo
raise ValueError('x must be between 0 and 1')
ValueError: x must be between 0 and 1
I think this code would do what all you want!
import random
def foo(x=0.5):
if x=="pick for me":
return random.random()
else:
return x
print foo()
print foo("pick for me")
print foo(0.3)
Assuming the parameter type passed is float (can also be checked), you could define that any input < 0 or > 1 means "pick for me"...
It also kind of validates that the passed parameter is within range, and if not generates a proper value.
But this does not really answer the question about choosing between a number & a string ;-) (type() could help for this)
def foo(x=0.5):
if 0 <= x <= 1:
return x
else:
return complicated_algorithm_that_picks_x()
Let's say I made an integer wrapper class in Python, like Java has. Most of the methods would be pretty trivial. But the __ eq __ overload (the equality test) poses a fun puzzle: consider the following method
def __eq__( self, other ):
return self.value == int(other)
Implementation details:
The Integer Wrapper has one field, "value," which is an integer
The method __ trunc __ returns the field "value" so that int( Integer(x) ) = x
The constructor for Integer truncates "value" to an integer; Integer(3.1)=Integer(3)
Rules for the Method
Integer(x) == Integer(x) must return true for all integers x
Integer(x) == x must return true for all integers x
Integer(x) == Integer(y) must return false for all inegers (x,y) such that x != y
Integer(x) == y must return false for all x != y
My beautiful method can be susceptible to the very last test. Consider
Integer(1) == 1.1
Will return true.
How can we implement an Integer class under the stated constraints - something that seems trivial, with the stated fairly straight-forward definition of equality?
Note: you might find it bothersome that I claim Integer(1.1) == Integer(1) is a valid result. I'll admit it has some silliness to it, but I have control over how the constructor handles non-integer parameters; I could throw an exception if I wanted claiming unsafe cast. I don't have any control over the fourth case, in which someone asks if my integer equals a primitive of the same value.
Edit
Per request, here's enough code for the class that I think the conditions I've set forth are satisfied:
class Integer:
""" An integer wrapper class. Provides support for using integers as
objects. There are subtelties and pitfalls, however. """
def __init__( self, val = 0 ):
""" Constructs a new integer with a certain value """
self.val = int(val)
def __trunc__( self ):
""" Truncates the internal value """
return int(self.val)
def __eq__( self, other ):
""" Returns true if the object ahs the same value as this object """
return self.val == int(other)
If I interpreted your requirements correctly, this should do it:
>>> class Integer:
... def __init__(self, val=0):
... self.val = int(val)
... def __eq__(self, other):
... return self.val == other
...
>>> Integer(1) == 1.1
False
>>> Integer(1.2) == Integer(1.3)
True
>>> Integer(4) == Integer(7)
False
>>> Integer(2) == 2
True
>>>
If I understand you correctly the issue here is that the value you compare against might be a float, rather than an int, that, in return, would get truncated to an equal int, if compared against.
If that is the case how about checking, if the compared value has a remainder when divided by the comparing value and reacting upon that:
def __eq__( self, other ):
if float(other) % self.value > 0:
return False
else:
return True
That way you can pass in a float that is divisible by self.value(), or the same value as an integer and you return true for all cases where
int(x) == y || int(x) == int(y) || int(x) == float(y), for all x / y = 1
Just 5 days into Python, learning through Code Academy. I have no knowledge of any other language (very little knowledge of Ruby!).
What am I doing wrong with this code?
Q: Write a function, by_three, that calls a second function, cube,
if a number is evenly divisible by 3 and "False" otherwise. You should
then return the result you get from cube. As for cube, that function
should return the cube of the number passed from by_three. (Cubing a
number is the same as raising it to the third power).
So, for example, by_three should take 9, determine it's evenly
divisible by 3, and pass it to cube, who returns 729 (the result of
9**3). If by_three gets 4, however, it should return False and leave
it at that.
Lastly, call by_three on 11, 12, and 13 on three separate lines.
ANS:
def by_three(n):
orig_num = n
if (isinstance(orig_num, int) and orig_num%3 == 0 ):
cube(orig_num)
else:
print "False"
def cube(orig_num):
cube = orig_num**3
print cube
return
by_three(11)
by_three(12)
by_three(13)
When I run the above code, here is what I get. Why do these values appear in this way?
False
1728
False
==> None
False
False
1728
Oops, try again.
I can't say why you're seeing odd results. When I copy your code into the interpreter, I see:
>>> def by_three(n):
... orig_num = n
... if (isinstance(orig_num, int) and orig_num%3 == 0 ):
... cube(orig_num)
... else:
... print "False"
...
>>> def cube(orig_num):
... cube = orig_num**3
... print cube
... return
...
>>> by_three(11)
False
>>> by_three(12)
1728
>>> by_three(13)
False
I think this problem is a lot simpler than you're making it, though. It's hard to tell because the question is rather poorly written, but this would be my answer:
def by_three(n): return False if n % 3 else cube(n)
def cube(n): return n**3
by_three(11)
by_three(12)
by_three(13)
And this is what it looks like in the interpreter:
>>> def by_three(n): return False if n % 3 else cube(n)
...
>>> def cube(n): return n**3
...
>>> by_three(11)
False
>>> by_three(12)
1728
>>> by_three(13)
False
First you need to change the cube function to actually return the cube. And you can even simplify your cube method, by just returning the cube without storing the temporary result: -
def cube(orig_num):
return orig_num**3 # Just return the cube
Then in your by_three function, rather than printing "False", you should return it. Also, return the value returned by cube function: -
def by_three(n):
if (isinstance(n, int) and n % 3 == 0):
return cube(n) # Added return here
else:
return False #Changed print to return.
You can also simplify this method to just a single line return statement. You should use try-except block instead of checking instance with isinstance, if you are passing a value in your function: -
def by_three(n):
try:
return cube(n) if n % 3 == 0 else False
except TypeError, e:
return False
And then, when you invoke the method, print the result obtained: -
print by_three(11)
print by_three(12)
print by_three(13)
In your cube method, you're not actually returning anything. Unlike Ruby, which returns the last statement in the block, you have to explicitly state what you're returning. There's also no need to create defensive copies of the value. Furthermore, you can't reuse the name cube, or that will clobber your method definition.
def cube(orig_num):
return orig_num ** 3
Next, you would have to return the values in your caller method. I'll leave that as an exercise for you - shouldn't be too tricky to figure out from here.
Third, you (sort of) don't need to worry if the number is an int or a float. While there is imprecision with floating point numbers, the values shouldn't be perfectly divisible by 3.