about
Bits Objects Python
11/26/2023
0
Synopsis:
This page demonstrates uses of Python User-Defined types and their objects. The purpose is to quickly acquire some familiarity with user-defined types and their implementations.- Python defines a few special class methods: parameterized constructors, and other operators for indexing and comparison etc.
- The compiler does not generate any of these special methods.
- Also, this is the first set of examples to partition code into several files. That supports readability, may improve translation times, and makes maintenance significantly easier.
Demo Notes
1.0 Source Code
- PointsObj.py defines a custom Point4D class
- Py_Objects.py demonstrates instances of a few library types, an instance of Point4D, and a small demo of reference behavior.
- AnalysisObj.py provides functions that help analyze and display operations on these types.
1.1 Point4D
#------------------------------------------------
# Py_Objects::PointsObj.py
# - User-defined space-time Point class
#------------------------------------------------
import datetime
import time
# point class with three spatial coordinates
class Point4D:
x = 0.0
y = 0.0
z = 0.0
t = datetime.datetime.now()
# supports constructor notation
def __init__(self) -> None:
pass
# show named value of Point4D instance
def show(self, name) :
print("{} {{".format(name))
print(" x:{}, y:{}, z:{}".format(self.x, self.y, self.z))
print(" t:{}".format(self.t))
print("}")
Concept:
Implementation:
Code layout:
1.2 Source Code Structure
import sys
import copy
import AnalysisObj
import PointsObj
# Python/Py_Objects::Py_Objects.py
#
# Python Dynamic Data Types
# int, float, complex
# bytes, bytearray, memoryview
# list, tuple, range
# dict, set, frozenset
# bool
# str
# NoneType
# define alias shortcut
anal = AnalysisObj
# # Python requires definition before use ordering
#-- Demonstrate primitive and library types ---------------
def demolibtypes() :
# code elided
#-- demonstrate user-define type --------------------------
def demouserdeftype() :
# code elided
# -- illustrate reference behavior ------------------------
def demorefbehavior() :
# code elided
# -- Demonstration starts here ----------------------------
def execute() :
print(" Demonstrate Python Objects")
print("----------------------------")
print()
anal.showNote(
" All Python types are reference-based\n"\
" with values in the managed heap. That\n"\
" has consequences we explore in this demo."
)
print()
demolibtypes()
demouserdeftype()
demorefbehavior()
print("\nThat's all folks!\n")
execute()
Structure:
Execution Flow:
1.3 Primitive and Library types
#-- Demonstrate primitive and library types ---------------
def demolibtypes() :
anal.showNote(" primitive and library types","\n")
# type of d1 is inferred as float from RHS
d1 = 3.1415927
print("d1 = ", d1)
# repr(s) wraps string s in quotes
s1 = "a string"
print("s1 = ", repr(s1))
anal.showOp("s2 = s1")
s2 = s1
anal.showIdent(s1, "s1")
anal.showIdent(s2, "s2")
#print("s2 = {}".format(s2))
anal.showOp("s2 += \" and more\"")
s2 += " and more"
anal.showIdent(s2, "s2")
anal.showIdent(s1, "s1")
print()
anal.showNote(
" Assignment, in Python, assigns references not\n"\
" values. So s1 and s2 share same heap instance\n"\
" But strings are immutable. So when a change is\n"\
" made to one, that creates a new changed instance\n"\
" without changing the original."
)
print()
l1 = ["you", "me", "them", "us"]
anal.showValueEnum(l1, "l1")
print("\nl1 = ", l1, "\n")
anal.showOp("l2 = l1")
l2 = l1
anal.showOp('l2.append("everyone")')
l2.append("everyone")
print("\nl2 = ", l2)
print("l1 = ", l1, "\n")
anal.showOp('l2[1] = "myself"')
l2[1] = "myself"
print("\nl2 = ", l2)
print("l1 = ", l1)
print()
anal.showNote(
" Changes to target of assignment affect source\n"\
" except for immutable strings."\
" \"caveat emptor\""
)
print()
--------------------------------------------------
primitive and library types
--------------------------------------------------
d1 = 3.1415927
s1 = 'a string'
--- s2 = s1 ---
s1 a string 1974941848240
s2 a string 1974941848240
--- s2 += " and more" ---
s2 a string and more 1974942127632
s1 a string 1974941848240
--------------------------------------------------
Assignment, in Python, assigns references not
values. So s1 and s2 share same heap instance
But strings are immutable. So when a change is
made to one, that creates a new changed instance
without changing the original.
--------------------------------------------------
l1 {
you, me, them, us
}
l1 = ['you', 'me', 'them', 'us']
--- l2 = l1 ---
--- l2.append("everyone") ---
l2 = ['you', 'me', 'them', 'us', 'everyone']
l1 = ['you', 'me', 'them', 'us', 'everyone']
--- l2[1] = "myself" ---
l2 = ['you', 'myself', 'them', 'us', 'everyone']
l1 = ['you', 'myself', 'them', 'us', 'everyone']
--------------------------------------------------
Changes to target of assignment affect source
except for immutable strings. "caveat emptor"
--------------------------------------------------
1.4 User-defined Type
#-- demonstrate user-define type --------------------------
def demouserdeftype() :
anal.showNote(" user defined type","\n")
anal.showOp("p1a = Point4D()")
p1a = PointsObj.Point4D()
anal.showOp("AnalysisObj.showTypeShowable(p1a, \"p1a\", nl)")
# function defined in AnalysisObj.py
anal.showTypeShowable(p1a, "p1a", "\n")
p1a.x = 2
p1a.y = -3.5
p1a.z = -42
print()
# method defined in Point4D
p1a.show("p1a")
anal.showOp("p1b = p1a")
p1b = p1a # assignment of reference - no copy of instance.
p1b.show("p1b")
anal.showOp("p1b.y = 13")
p1b.y = 13
p1b.show("p1b")
p1a.show("p1a")
anal.showNote(
" Reference assigned, not value. So change\n"\
" in P1b changed source p1a."
)
print()
# copy.deepcopy(p) copies entire object graph of p
anal.showOp("p1c = copy.deepcopy(p1b)")
p1c = copy.deepcopy(p1b)
p1c.show("p1c");
p1b.show("p1b")
anal.showOp("p1c.z = 12")
p1c.z = 12
p1c.show("p1c")
p1b.show("p1b")
anal.showNote(
" p1c.z reference assigned, not value. But no\n"\
" change in p1b since p1c is deep clone of p1b."
)
print()
--------------------------------------------------
user defined type
--------------------------------------------------
--- p1a = Point4D() ---
--- AnalysisObj.showTypeShowable(p1a, "p1a", nl) ---
p1a <class 'PointsObj.Point4D'> dynamic
p1a {
x:0.0, y:0.0, z:0.0
t:2024-01-14 18:41:57.225653
}
p1a {
x:2, y:-3.5, z:-42
t:2024-01-14 18:41:57.225653
}
--- p1b = p1a ---
p1b {
x:2, y:-3.5, z:-42
t:2024-01-14 18:41:57.225653
}
--- p1b.y = 13 ---
p1b {
x:2, y:13, z:-42
t:2024-01-14 18:41:57.225653
}
p1a {
x:2, y:13, z:-42
t:2024-01-14 18:41:57.225653
}
--------------------------------------------------
Reference assigned, not value. So change
in P1b changed source p1a.
--------------------------------------------------
--- p1c = copy.deepcopy(p1b) ---
p1c {
x:2, y:13, z:-42
t:2024-01-14 18:41:57.225653
}
p1b {
x:2, y:13, z:-42
t:2024-01-14 18:41:57.225653
}
--- p1c.z = 12 ---
p1c {
x:2, y:13, z:12
t:2024-01-14 18:41:57.225653
}
p1b {
x:2, y:13, z:-42
t:2024-01-14 18:41:57.225653
}
--------------------------------------------------
p1c.z reference assigned, not value. But no
change in p1b since p1c is deep clone of p1b.
--------------------------------------------------
1.5 Reference Behavior
# -- illustrate reference behavior ------------------------
def demorefbehavior() :
# reference behavior - new child object
anal.showOp("t5 = (1, 2, 3)")
t5 = (1, 2, 3)
anal.showIdent(t5, "t5")
anal.showOp("t6 = [1, t5, \"weird\"]")
t6 = [1, t5, "weird"]
anal.showIdent(t6, "t6")
anal.showType(t6, "t6")
print("-- t5 = 1 + 1j : new object --")
#new object for t5
t5 = 1 + 1j
anal.showIdent(t5, "t5")
# t6 still refers to old t5 object
anal.showIdent(t6, "t6")
print()
anal.showNote(
" new object for t5, t6 not affected", "\n"
)
# # reference behavior - iterate over children
print("-- iterate over t6 children --")
for i in t6:
anal.showIdent(i, "elem")
# uncommenting the two statements below shows all the user
# and system defined methods
# print("\n-- iterate over t6 methods --")
# print(dir(t6))
--- t5 = (1, 2, 3) ---
t5 (1, 2, 3) 1974942114176
--- t6 = [1, t5, "weird"] ---
t6 [1, (1, 2, 3), 'weird'] 1974941790912
t6 <class 'list'> dynamic
value: [1, (1, 2, 3), 'weird'] , size: 80
-- t5 = 1 + 1j : new object --
t5 (1+1j) 1974941491280
t6 [1, (1, 2, 3), 'weird'] 1974941790912
--------------------------------------------------
new object for t5, t6 not affected
--------------------------------------------------
-- iterate over t6 children --
elem 1 1974940467440
elem (1, 2, 3) 1974942114176
elem weird 1974942119920
1.6 Analysis and Display Functions:
#------------------------------------------------
# Py_Objects::AnalysisObj.py
# - collection of display and analysis functions
#------------------------------------------------
import sys
# Python requires definition before use ordering
# show name, type, value, and size of a Python instance
def showType(t, nm: str, suffix: str = "") :
print(nm, type(t), "dynamic")
print("value: ", t, ', size: ', sys.getsizeof(t), suffix)
# generate indent string with n spaces
def indent(i):
tmpStr = ""
for i in range(i):
tmpStr += ' '
return tmpStr
# fold indexable into rows of width elements indented by
# left spaces
def fold(enum, left, width):
tmpStr = indent(left)
for i in range(len(enum)):
tmpStr += str(enum[i]) + ", "
if(((i + 1) % width) == 0 and i != 0):
tmpStr += "\n" + indent(left)
rIndex = tmpStr.rindex(',')
tmpStr = tmpStr[:rIndex]
return tmpStr
# show name, type, value, and size of a Python instance
def showTypeEnum(enum, nm, left = 0, width = 7, suffix = "") :
# topStr = indent(left) + nm + type(enum) + "dynamic"
print(indent(left),nm, ' ', type(enum), ' ', "dynamic", sep='')
print(indent(left), "{", sep='')
print(fold(enum, left+2, width))
print(indent(left), "}", sep = '')
print(indent(left), "size: ", sys.getsizeof(enum), suffix, sep='')
# show value of an enumerable Python instance
def showValueEnum(enum, nm, left = 0, width = 7, suffix = "") :
# topStr = indent(left) + nm + type(enum) + "dynamic"
print(indent(left),nm, ' ', sep='', end='')
print("{", sep='')
print(fold(enum, left+2, width))
print(indent(left), "}", sep = '')
# same as showType except uses class method to show value
def showTypeShowable(t, nm, suffix = ""):
print(nm, type(t), "dynamic")
t.show(nm)
# show Python id, unique for each instance
def showIdent(t, n, suffix = "") :
print(n, t, id(t), suffix)
# show emphasized note
def showNote(text, suffix = "", n: int = 50) :
tmpStr = ""
for i in range(n):
tmpStr += '-'
print(tmpStr)
print(text)
print(tmpStr, suffix)
# show delineated string to announce a program operation
def showOp(text):
print("--- {} ---".format(text))
Functions:
2.0 Build
C:\github\JimFawcett\Bits\Python\Py_Objects
> python Py_Objects.py
Demonstrate Python Objects
----------------------------
# remaining output elided
3.0 VS Code
4.0 References
Reference | Description |
---|---|
Python Tutorial - w3schools | Interactive examples |
Python Reference - docs.python.org | Semi-formal syntax reference |