Home > Mobile >  Incorrect object method is invoked
Incorrect object method is invoked

Time:10-27

I have a python file which contains a abstract class and several sub-classes. The code looks like the following

# test.py

import threading
from abc import ABC, abstractmethod

class Job(object):
    def __init__(self, job_name):
        self.job_name = job_name

    def start(self):
        self.run()
        self.clean_up()

    @abstractmethod
    def run(self):
        pass

    @abstractmethod
    def clean_up(self):
        pass

class JobA(Job):
    def __init__(self):
        super().__init__('job_a')
        print('JobA constructed')

    def run(self):
        print('%s: run for %s' % (self.__str__(), self.job_name))

    def clean_up(self):
        print('%s: clean up for %s' % (self.__str__(), self.job_name))

class JobB(Job):
    def __init__(self):
        super().__init__('job_b')
        print('JobB constructed')

    def run(self):
        print('%s: run for %s' % (self.__str__(), self.job_name))

    def clean_up(self):
        print('%s: clean up for %s' % (self.__str__(), self.job_name))

if __name__ == '__main__':
    jobs = [JobA(), JobB()]
    threads = [threading.Thread(name=job.__str__(), target=lambda: job.start())  for job in jobs]
    print(threads)
    for t in threads:
        print('starting thread %s' % t.name)
        t.start()

The output of this code is

JobA constructed
JobB constructed
[<Thread(<__main__.JobA object at 0x1008a8f70>, initial)>, <Thread(<__main__.JobB object at 0x1008a8d90>, initial)>]
starting thread <__main__.JobA object at 0x1008a8f70>
<__main__.JobB object at 0x1008a8d90>: run for job_b
<__main__.JobB object at 0x1008a8d90>: clean up for job_b
starting thread <__main__.JobB object at 0x1008a8d90>
<__main__.JobB object at 0x1008a8d90>: run for job_b
<__main__.JobB object at 0x1008a8d90>: clean up for job_b

Although two threads are constructed for objects of both JobA and JobB, start() is called on object of JobB only.

I expect that start() should be called on different objects in the array. Are there any mistakes in this code?

Thanks.

My python version is Python 3.9.7 (default, Oct 12 2021, 22:38:23) [Clang 13.0.0 (clang-1300.0.29.3)] on darwin

CodePudding user response:

The lambda definition refers to the loop variable job of the list comprehension (i.e. it defines a closure). When the lambda is called, it will resolve that name job which now refers to the last element of the comprehension (i.e. Job B).

Instead you could use a default value, in order to bind each object of the comprehension correctly to the respective lambda function:

threads = [threading.Thread(name=job.__str__(), target=lambda j=job: j.start())  for job in jobs]

Consider the following example code for a better visualization:

>>> funcs = [lambda: print(x) for x in range(3)]
>>> for f in funcs:
...     f()
... 
2
2
2
>>> funcs = [lambda y=x: print(y) for x in range(3)]
>>> for f in funcs:
...     f()
... 
0
1
2
  • Related