@@ -260,23 +260,132 @@ All of the methods described below are executed atomically.
260260Thread-Local Data
261261-----------------
262262
263- Thread-local data is data whose values are thread specific. To manage
264- thread-local data, just create an instance of :class: ` local ` (or a
265- subclass) and store attributes on it ::
263+ Thread-local data is data whose values are thread specific. If you
264+ have data that you want to be local to a thread, create a
265+ :class: ` local ` object and use its attributes ::
266266
267- mydata = threading.local()
268- mydata.x = 1
267+ >>> mydata = local()
268+ >>> mydata.number = 42
269+ >>> mydata.number
270+ 42
269271
270- The instance's values will be different for separate threads.
272+ You can also access the :class: `local `-object's dictionary::
273+
274+ >>> mydata.__dict__
275+ {'number': 42}
276+ >>> mydata.__dict__.setdefault('widgets', [])
277+ []
278+ >>> mydata.widgets
279+ []
280+
281+ If we access the data in a different thread::
282+
283+ >>> log = []
284+ >>> def f():
285+ ... items = sorted(mydata.__dict__.items())
286+ ... log.append(items)
287+ ... mydata.number = 11
288+ ... log.append(mydata.number)
289+
290+ >>> import threading
291+ >>> thread = threading.Thread(target=f)
292+ >>> thread.start()
293+ >>> thread.join()
294+ >>> log
295+ [[], 11]
296+
297+ we get different data. Furthermore, changes made in the other thread
298+ don't affect data seen in this thread::
299+
300+ >>> mydata.number
301+ 42
302+
303+ Of course, values you get from a :class: `local ` object, including their
304+ :attr: `~object.__dict__ ` attribute, are for whatever thread was current
305+ at the time the attribute was read. For that reason, you generally
306+ don't want to save these values across threads, as they apply only to
307+ the thread they came from.
308+
309+ You can create custom :class: `local ` objects by subclassing the
310+ :class: `local ` class::
311+
312+ >>> class MyLocal(local):
313+ ... number = 2
314+ ... def __init__(self, /, **kw):
315+ ... self.__dict__.update(kw)
316+ ... def squared(self):
317+ ... return self.number ** 2
318+
319+ This can be useful to support default values, methods and
320+ initialization. Note that if you define an :py:meth: `~object.__init__ `
321+ method, it will be called each time the :class: `local ` object is used
322+ in a separate thread. This is necessary to initialize each thread's
323+ dictionary.
324+
325+ Now if we create a :class: `local ` object::
326+
327+ >>> mydata = MyLocal(color='red')
328+
329+ we have a default number::
330+
331+ >>> mydata.number
332+ 2
333+
334+ an initial color::
335+
336+ >>> mydata.color
337+ 'red'
338+ >>> del mydata.color
339+
340+ And a method that operates on the data::
341+
342+ >>> mydata.squared()
343+ 4
344+
345+ As before, we can access the data in a separate thread::
346+
347+ >>> log = []
348+ >>> thread = threading.Thread(target=f)
349+ >>> thread.start()
350+ >>> thread.join()
351+ >>> log
352+ [[('color', 'red')], 11]
353+
354+ without affecting this thread's data::
355+
356+ >>> mydata.number
357+ 2
358+ >>> mydata.color
359+ Traceback (most recent call last):
360+ ...
361+ AttributeError: 'MyLocal' object has no attribute 'color'
362+
363+ Note that subclasses can define :term: `__slots__ `, but they are not
364+ thread local. They are shared across threads::
365+
366+ >>> class MyLocal(local):
367+ ... __slots__ = 'number'
368+
369+ >>> mydata = MyLocal()
370+ >>> mydata.number = 42
371+ >>> mydata.color = 'red'
372+
373+ So, the separate thread::
374+
375+ >>> thread = threading.Thread(target=f)
376+ >>> thread.start()
377+ >>> thread.join()
378+
379+ affects what we see::
380+
381+ >>> mydata.number
382+ 11
271383
272384
273385.. class :: local()
274386
275387 A class that represents thread-local data.
276388
277- For more details and extensive examples, see the documentation string of the
278- :mod: `!_threading_local ` module: :source: `Lib/_threading_local.py `.
279-
280389
281390.. _thread-objects :
282391
0 commit comments