on implementing a toy oop-system
Meredith Montgomery
mmontgomery at levado.to
Sun Sep 4 17:05:31 EDT 2022
Just for investigation sake, I'm trying to simulate OO-inheritance.
(*) What did I do?
I decided that objects would be dictionaries and methods would be
procedures stored in the object. A class is just a procedure that
creates such object-dictionary. So far so good. Trouble arrived when I
decided to have inheritance. The problem might related to the
lexical-nature of closures, but I'm not sure.
(*) A problem
Say I have a Counter-creating object procedure and a Car-creating object
procedure. Cars want to track how many times they park, so they inherit
a counter.
--8<---------------cut here---------------start------------->8---
def Car(maker = None, color = None):
o = inherit(Object(), Counter())
o["maker"] = maker
o["color"] = color
o["speed"] = 0
--8<---------------cut here---------------end--------------->8---
What Object() does is just to return a dictionary. What inherit() does
is just to merge the two dictionaries into a single one (to be used by
the Car procedure).
This didn't come out as I expected, though. Let me show you
step-by-step where the problem is. First, here's a counter in action.
>>> c1 = Counter()
>>> c1["inc"]()["inc"]()
{'id': <function Object.<locals>.id at 0x0000016547C648B0>, 'n': 2, [...]}
>>> c1["n"]
2
That's good and expected: I incremented it twice. But look what happens
with a Car's counter.
>>> c = Car()
>>> c = Car()
>>> c
{'id': <function Object.<locals>.id at 0x0000016547C64B80>, 'n': 0,
'inc': <function Counter.<locals>.inc at 0x0000016547C64C10>, 'dec':
<function Counter.<locals>.dec at 0x0000016547C64CA0>, 'maker': None,
'color': None, 'speed': 0, 'accelerate': <function
Car.<locals>.accelerate at 0x0000016547C64AF0>, 'park': <function
Car.<locals>.park at 0x0000016547C64D30>}
>>> c["inc"]()
{'id': <function Object.<locals>.id at 0x0000016547C64B80>, 'n': 1,
'inc': <function Counter.<locals>.inc at 0x0000016547C64C10>, 'dec':
<function Counter.<locals>.dec at 0x0000016547C64CA0>}
We can see something got incremented! But...
>>> c["n"]
0
Indeed, what got incremented is the dictionary attached to the /inc/
procedure of the Counter closure, so it's that dictionary that's being
mutated. My /inherit/ procedure is not able to bring that procedure
into the Car dictionary.
Is that at all possible somehow? Alternatively, how would you do your
toy oop-system?
(*) Full code below
from random import random
def Object():
myid = random()
def id():
return myid
return {
"id": id
}
def inherit(o, parentObject):
o.update(parentObject)
return o
def Counter(begin_at = 0):
o = Object()
o["n"] = begin_at
def inc():
nonlocal o
o["n"] += 1
return o
o["inc"] = inc
def dec():
nonlocal o
o["n"] -= 1
return o
o["dec"] = dec
return o
def Car(maker = None, color = None):
o = inherit(Object(), Counter())
o["maker"] = maker
o["color"] = color
o["speed"] = 0
def accelerate(speed):
nonlocal o
print(f"Car-{o['id']()}: accelerating to {speed}...")
o["speed"] = speed
return o
o["accelerate"] = accelerate
def park():
nonlocal o
o["speed"] = 0
o["parked"] = True
o["inc"]()
print(f"Car-{o['id']()}: parked! ({o['n']} times)")
return o
o["park"] = park
return o
def tests():
c1 = Counter()
c2 = Counter(100)
c1["inc"]()["inc"]()
c2["dec"]()["dec"]()["dec"]()
print("c1 is 2:", c1["n"])
print("c2 is 97:", c2["n"])
car1 = Car("VW", "Red")
car1["accelerate"](100)
print("speed is 100:", car1["speed"])
car2 = Car("Ford", "Black")
car2["accelerate"](120)["park"]()
car2["accelerate"](50)["park"]()
print("has parked 2 times:", car2["n"])
More information about the Python-list
mailing list