Class Notes: Data Types and Operators


  1. Some Builtin Types
  2. Some Builtin Constants
  3. Some Builtin Operators
  4. Integer Division
  5. The Modulus or Remainder operator (%)
  6. More of the Modulus or Remainder operator (%)
  7. Types Affect Semantics
  8. Operator Order (Precedence and Associativity)
  9. Approximate Values of Floating-Point Numbers
  10. Short-Circuit Evaluation
  11. type vs isinstance


  1. Some Builtin Types
  2. import math def f(): print("This is a user-defined function") return 42 print("Some basic types in Python:") print(type(2)) # int print(type(2.2)) # float print(type(2 < 2.2)) # bool (boolean) print(type(type(42))) # type print("#####################################################") print("And some other types we may see later in the course...") print(type("2.2")) # str (string or text) print(type([1,2,3])) # list print(type((1,2,3))) # tuple print(type({1,2})) # set print(type({1:42})) # dict (dictionary or map) print(type(2+3j)) # complex (complex number) print(type(f)) # A function is also a value!

  3. Some Builtin Constants
  4. print("Some builtin constants:") print(True) print(False) print(None) print("And some more constants in the math module:") import math print(math.pi) print(math.e)

  5. Some Builtin Operators
  6. Note: for now at least, we are not covering the bitwise operators (<<, >>, &, |, ^, ~, &=, |=, ^=).

  7. Integer Division (//)

  8. Note: This operator is very useful on the homework, so spend some time experimenting with how it works!
    print("The / operator does 'normal' float division:") print(" 5/3 =", ( 5/3)) print() print("The // operator does integer division:") print(" 5//3 =", ( 5//3)) print(" 2//3 =", ( 2//3)) print("-1//3 =", (-1//3)) print("-4//3 =", (-4//3))

  9. The Modulus or Remainder Operator (%)

  10. Note: This is another very useful operator. It's worth taking the time to try it out.
    print(" 6%3 =", ( 6%3)) print(" 5%3 =", ( 5%3)) print(" 2%3 =", ( 2%3)) print(" 0%3 =", ( 0%3)) print("-4%3 =", (-4%3)) print(" 3%0 =", ( 3%0))

  11. More of the Modulus or Remainder Operator (%)
  12. Verify that (a%b) is equivalent to (a - (a//b)*b):
    def mod(a, b): return a - (a//b)*b print(41%14, mod(41,14)) print(14%41, mod(14,41)) print(-32%9, mod(-32,9)) print(32%-9, mod(32,-9))

  13. Types Affect Semantics

  14. Operators behave differently depending on the data types they interact with
    print(3 * 2) print(3 * "abc") print(3 + 2) print("abc" + "def") print(3 + "def")

  15. Operator Order (Precedence and Associativity)
  16. print("Precedence:") print(2+3*4) # prints 14, not 20 print(5+4%3) # prints 6, not 0 (% has same precedence as *, /, and //) print(2**3*4) # prints 32, not 4096 (** has higher precedence than *, /, //, and %) print() print("Associativity:") print(5-4-3) # prints -2, not 4 (- associates left-to-right) print(4**3**2) # prints 262144, not 4096 (** associates right-to-left)

  17. Approximate Values of Floating-Point Numbers
    print(0.1 + 0.1 == 0.2) # True, but... print(0.1 + 0.1 + 0.1 == 0.3) # False! print(0.1 + 0.1 + 0.1) # prints 0.30000000000000004 (uh oh) print((0.1 + 0.1 + 0.1) - 0.3) # prints 5.55111512313e-17 (tiny, but non-zero!)

    Equality Testing with almostEqual:
    print("The problem....") d1 = 0.1 + 0.1 + 0.1 d2 = 0.3 print(d1 == d2) # False (never use == with floats!) print() print("The solution...") epsilon = 10**-10 print(abs(d2 - d1) < epsilon) # True! print() print("Once again, using a useful helper function, almostEqual:") def almostEqual(d1, d2): epsilon = 10**-10 return (abs(d2 - d1) < epsilon) d1 = 0.1 + 0.1 + 0.1 d2 = 0.3 print(d1 == d2) # still False, of course print(almostEqual(d1, d2)) # True, and now packaged in a handy reusable function!
  18. Short-Circuit Evaluation
  19. def yes(): return True def no(): return False def crash(): return 1/0 # crashes! print(no() and crash()) # Works! print(crash() and no()) # Crashes! print (yes() and crash()) # Never runs (due to crash), but would also crash (without short-circuiting)

    Once again, using the "or" operator:
    def yes(): return True def no(): return False def crash(): return 1/0 # crashes! print(yes() or crash()) # Works! print(crash() or yes()) # Crashes! print(no() or crash()) # Never runs (due to crash), but would also crash (without short-circuiting)

    Yet another example:
    def isPositive(n): result = (n > 0) print("isPositive(",n,") =", result) return result def isEven(n): result = (n % 2 == 0) print("isEven(",n,") =", result) return result print("Test 1: isEven(-4) and isPositive(-4))") print(isEven(-4) and isPositive(-4)) # Calls both functions print("----------") print("Test 2: isEven(-3) and isPositive(-3)") print(isEven(-3) and isPositive(-3)) # Calls only one function!

  20. type vs isinstance
  21. # Both type and isinstance can be used to type-check # In general, (isinstance(x, T)) will be more robust than (type(x) == T) print(type("abc") == str) print(isinstance("abc", str)) # We'll see better reasons for this when we cover OOP + inheritance later # in the course. For now, here is one reason: say you wanted to check # if a value is any kind of number (int, float, complex, etc). # You could do: def isNumber(x): return ((type(x) == int) or (type(x) == float)) # are we sure this is ALL kinds of numbers? print(isNumber(1), isNumber(1.1), isNumber(1+2j), isNumber("wow")) # But this is cleaner, and works for all kinds of numbers, including # complex numbers for example: import numbers def isNumber(x): return isinstance(x, numbers.Number) # works for any kind of number print(isNumber(1), isNumber(1.1), isNumber(1+2j), isNumber("wow"))