A test suggests that calling a method from the Timer works just fine. I confess that I find it a bit hard to grok at first.
I wrote this test:
def test_with_method(self): checker = Checker(19) another = Checker(9) some_value = 31 timer = Timer(1, checker.set, some_value) timer2 = Timer(1, another.set, 21) timer.tick(1.1) assert checker.happened == 31 + 19 timer2.tick(1.1) assert another.happened == 21 + 9
It uses this class:
class Checker(): def __init__(self, extra): self.happened = 0 self.extra = extra if extra else 0 def set(self, value): self.happened = value + self.extra return True
For the test to pass, the two timers need to be referencing different instances of the class, the one with
extra equal to 19, the other with it equal to 9, and with different input values to
set, 31 and 21 respectively.
The test runs. What I don’t understand is how it gets the right instance. the only clue it gets is the difference between
another.set. That should be a function.
Let’s print those.
<bound method Checker.set of <test_timer.Checker object at 0x1058cd310>> <bound method Checker.set of <test_timer.Checker object at 0x1058cd2d0>>
So, nice. Obviously a “bound method” is a function that knows the specific object it is bound to. Just what we hoped for, and what the test suggested must have happened. I haven’t found any good documentation on what’s going on, but clearly what happens is that the compiler sees
checker must surely be known to it at that point, so it assumes
checker to be an object and creates the “bound method” as a little package that calls the function, passing the object as the first,
We don’t have to think about it: we just have to know that it works.
Let’s review how we’re doing the zigzag in Saucer:
def set_zig_timer(self): def zig(saucer): saucer.velocity = saucer.new_direction() * saucer.direction return True # noinspection PyAttributeOutsideInit self.zig_timer = Timer(u.SAUCER_ZIG_TIME, zig, self)
We can make this more clear, I think, like this:
def set_zig_timer(self): # noinspection PyAttributeOutsideInit self.zig_timer = Timer(u.SAUCER_ZIG_TIME, self.zig_zag_action) def zig_zag_action(self): self.velocity = self.new_direction() * self.direction return True
That works as advertised. And it’s definitely nicer to be able to define the action as a method on the object. I just invented the convention of naming them
something_action as a reminder to return T/F.
A noticeable improvement! See you next time!