In [2]:
# in languages like C, each variable has a type.
# in Python, each value has a type, and a variable can have any value
# the "type" builtin function returns the type of an object

str(3)


Out[2]:
'3'

In [3]:
v = 'fish'
type(v)


Out[3]:
str

In [4]:
v = 3
type(v)


Out[4]:
int

In [5]:
# suppose you want a function that behaves differently depending on what
# type of object it receives as an argument. You can do it this way:

def count(thing):
    if type(thing) == str:
        return range(len(thing))
    elif type(thing) == int:
        return range(thing)
    
count('fish')


Out[5]:
[0, 1, 2, 3]

In [6]:
count(4)


Out[6]:
[0, 1, 2, 3]

In [7]:
# this is not the recommended way to do it, for a variety of complex reasons having to
# do with how types are defined and referred to. the more idiomatic approach is "duck typing"

# duck typing is based on the idea that if it walks like a duck and quacks like a duck, it's a duck.

# to duck type, take an action that assumes that a value is a given type, and if it raises an exception,
# then drop that assumption and move on to trying other actions.

# first, let's reimplement count assuming that "thing" has a length:

def count(thing):
    return range(thing)

count('fish')


---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-7-daa31fe9e0f2> in <module>()
     12     return range(thing)
     13 
---> 14 count('fish')

<ipython-input-7-daa31fe9e0f2> in count(thing)
     10 
     11 def count(thing):
---> 12     return range(thing)
     13 
     14 count('fish')

TypeError: range() integer end argument expected, got str.

In [8]:
# now let's handle the case where thing has a length

def count(thing):
    try:
        return range(thing)
    except TypeError:
        pass
    return range(len(thing))

count('fish')


Out[8]:
[0, 1, 2, 3]

In [16]:
count(4)


Out[16]:
[0, 1, 2, 3]

In [9]:
# this still does not work.

count(3.7)


---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-9-4f76a4acc0b2> in <module>()
      1 # this still does not work.
      2 
----> 3 count(3.7)

<ipython-input-8-62488b9b0c47> in count(thing)
      6     except TypeError:
      7         pass
----> 8     return range(len(thing))
      9 
     10 count('fish')

TypeError: object of type 'float' has no len()

In [10]:
# let's extend the duck typing to include floats
import math

def count(thing):
    try:
        return range(thing)
    except TypeError:
        pass
    try:
        return range(len(thing))
    except TypeError:
        pass
    try:
        return range(int(math.ceil(thing)))
    except TypeError:
        pass
    raise TypeError
        
count(4.3)


Out[10]:
[0, 1, 2, 3, 4]

In [ ]:
# duck typing is based on the idea that types in Python are best thought of
# not as categorical definitions of allowable values, but rather sets of allowable
# actions that are associated with some type.