Am 21.08.2013 20:58, schrieb Johannes Bauer:
On 21.08.2013 11:11, Ulrich Eckhardt wrote:

That said, there is never a need for deriving
from the Thread class, you can also use it to run a function without
that. That way is IMHO clearer because the threading.Thread instance is
not the thread, just like a File instance is not a file. Both just
represent handles for manipulating the actual thing.

Huh? That I find most curious.

I *always* derive from threading.Thread and really like the way that
thread setup works (instanciate Thread handle, call start). Very
intuitive, never had the problems with clarity that you mentioned. Could
you elaborate on your suggestion? I don't seem to quite get it I'm afraid.

What is clear, convenient or not is largely a matter of taste. I'll try to explain my motivations though, maybe it helps...


Firstly, there is one observation: The Python object of type Thread is one thing, the actual thread is another thing. This is similar to the File instance and the actual file. The Python object represents the other thing (thread or file) but it "is not" this thing. It is rather a handle to the file or thread. This is different for e.g. a string, where the Python object is the string.

Due to this pairing between the actual thing and the handle, there is also some arity involved. For a single thread or file, there could be multiple Python objects for handling it, or maybe even none. When the Python object goes away, it doesn't necessarily affect the thread or file it represents. This already casts a doubt on the habit of deriving from the Thread type, just like deriving from the File type is highly unusual, as you are just deriving from a handle class.


Secondly, a thread is even less a "thing" than a file but rather a process or an ongoing operation. As such, it runs code and uses data but it is neither code nor data. Also, it doesn't care which code or data it currently uses. Similarly, the code and data don't care which thread uses them (synchronization problems in multithreaded apps aside). You will find that most of the code called in a thread doesn't use the thread handle, which is another sign that it doesn't care. For that reason, it is unnecessary that "self" references a Thread object. This reduces coupling, as the same code could be called synchronously and asynchronously. The code shouldn't know or care from which thread it is called.

In some cases, I even find it unnecessary to have a "self" at all, a thread can just as well run a non-member function. Also, even if it runs a memberfunction initially, it doesn't have to eventually. I find that forcing an OOP approach on things is flawed (OOP is a tool and not a goal) and prefer to make this a decision, but that is a different (although slightly related) issue.


Thirdly, when you derive a class from Thread, you are exposing this baseclass' interface to the public, too, even if you don't intend to. This has both the unwanted aspect that you expose all public functions of the baseclass and that even if you mean "is a thread", it actually means "is a handle to a thread", which is even less expressive. Curously, you do that in order to override a single function that is only invoked once. I prefer passing "instance.function" as callable argument to a plain Thread instance for running this, which keeps the two nicely separate.

For example, I have a TCP client class here that uses a service thread to handle the data transfer. The fact that there is a background thread should not be of concern to the user of my TCP client class. If I extended this to use two threads, it would even be impossible to derive from Thread for both of them.


In summary, I find that modelling something to "use a thread" is much clearer than modelling it as "is a thread".

Greetings from Hamburg!

Uli

--
http://mail.python.org/mailman/listinfo/python-list

Reply via email to