杰瑞科技汇

Python线程传参,args该怎么用?

Of course! Passing arguments to a thread in Python is a fundamental concept. The key is to pass arguments to the target function of a threading.Thread object.

Python线程传参,args该怎么用?-图1
(图片来源网络,侵删)

Here's a complete guide, from the basics to best practices.

The Core Concept: The args Parameter

When you create a threading.Thread object, you provide it with:

  1. target: The function that the thread will execute.
  2. args: A tuple containing the positional arguments you want to pass to that target function.

Why a tuple? Because *args in a function definition expects a sequence of arguments, and a tuple is the perfect way to provide that sequence.


Basic Example: Passing Positional Arguments

Let's start with a simple function that takes two arguments: a message and a delay.

Python线程传参,args该怎么用?-图2
(图片来源网络,侵删)
import threading
import time
def print_message(message, delay):
    """Prints a message, then waits for a specified delay."""
    print(f"Thread {threading.current_thread().name}: Starting to print '{message}'")
    time.sleep(delay)
    print(f"Thread {threading.current_thread().name}: Finished printing '{message}'")
# --- Main part of the script ---
if __name__ == "__main__":
    # Create the first thread
    # Note: args is a tuple, so we use a comma even with a single item: ('Hello',)
    thread1 = threading.Thread(
        target=print_message,
        args=('Hello from Thread 1!', 2),
        name='Thread-Alpha'
    )
    # Create the second thread
    thread2 = threading.Thread(
        target=print_message,
        args=('Hello from Thread 2!', 1),
        name='Thread-Beta'
    )
    print("Starting threads...")
    thread1.start()
    thread2.start()
    # Wait for both threads to complete their execution before the main program exits
    thread1.join()
    thread2.join()
    print("All threads have finished.")

Output:

Starting threads...
Thread Thread-Alpha: Starting to print 'Hello from Thread 1!'
Thread Thread-Beta: Starting to print 'Hello from Thread 2!'
Thread Thread-Beta: Finished printing 'Hello from Thread 2!'
Thread Thread-Alpha: Finished printing 'Hello from Thread 1!'
All threads have finished.

Explanation:

  • We defined print_message(message, delay).
  • When creating thread1, we set args=('Hello from Thread 1!', 2). This tuple is unpacked and passed as positional arguments to print_message, so 'Hello from Thread 1!' becomes message and 2 becomes delay.
  • The name argument is just for making the thread's name more readable in logs.
  • thread.join() is crucial. It makes the main thread wait until the thread it's called on has finished. Without it, the main script might exit before the threads are done, and you might not see all the output.

Passing Keyword Arguments: The kwargs Parameter

What if your function uses keyword arguments (e.g., def my_func(a=1, b=2))? For that, you use the kwargs parameter, which stands for "keyword arguments".

kwargs must be a dictionary where keys are the argument names and values are the values to pass.

Python线程传参,args该怎么用?-图3
(图片来源网络,侵删)
import threading
import time
def worker_config(name, is_active, timeout):
    """Simulates a worker with a specific configuration."""
    status = "active" if is_active else "inactive"
    print(f"Worker '{name}' is {status}. Will run for {timeout} seconds.")
    time.sleep(timeout)
    print(f"Worker '{name}' has finished.")
if __name__ == "__main__":
    # Define the arguments in a dictionary
    config1 = {
        'name': 'Config-Worker-A',
        'is_active': True,
        'timeout': 3
    }
    config2 = {
        'name': 'Config-Worker-B',
        'is_active': False,
        'timeout': 1
    }
    # Create threads using kwargs
    thread1 = threading.Thread(target=worker_config, kwargs=config1)
    thread2 = threading.Thread(target=worker_config, kwargs=config2)
    print("Starting worker threads...")
    thread1.start()
    thread2.start()
    thread1.join()
    thread2.join()
    print("All worker threads have finished.")

Output:

Starting worker threads...
Worker 'Config-Worker-A' is active. Will run for 3 seconds.
Worker 'Config-Worker-B' is inactive. Will run for 1 seconds.
Worker 'Config-Worker-B' has finished.
Worker 'Config-Worker-A' has finished.
All worker threads have finished.

Combining args and kwargs

You can also pass both positional and keyword arguments to the same thread. The args tuple comes first, followed by the kwargs dictionary.

import threading
def flexible_function(a, b, c=10):
    print(f"Received: a={a}, b={b}, c={c}")
if __name__ == "__main__":
    # 'a' and 'b' will come from args
    # 'c' will come from kwargs
    thread = threading.Thread(
        target=flexible_function,
        args=(1, 2),      # For 'a' and 'b'
        kwargs={'c': 20}  # For 'c'
    )
    thread.start()
    thread.join()
    print("Thread finished.")

Output:

Received: a=1, b=2, c=20
Thread finished.

Best Practice: Using a dataclass or namedtuple

For more complex scenarios with many arguments, passing a long tuple or a messy dictionary can be hard to read and maintain. A modern and clean approach is to use a dataclass.

This makes your code self-documenting and less error-prone.

import threading
import time
from dataclasses import dataclass
# Define a structured way to hold thread arguments
@dataclass
class TaskParams:
    task_id: int
    data: str
    duration: float
def process_task(params: TaskParams):
    """Processes a task using the structured parameters."""
    print(f"Task {params.task_id}: Processing '{params.data}' for {params.duration}s.")
    time.sleep(params.duration)
    print(f"Task {params.task_id}: Done.")
if __name__ == "__main__":
    # Create instances of our dataclass
    task1_params = TaskParams(task_id=101, data="Upload Report", duration=2.5)
    task2_params = TaskParams(task_id=102, data="Clear Cache", duration=1.0)
    # Pass the dataclass instance as a single argument
    thread1 = threading.Thread(target=process_task, args=(task1_params,))
    thread2 = threading.Thread(target=process_task, args=(task2_params,))
    print("Starting tasks...")
    thread1.start()
    thread2.start()
    thread1.join()
    thread2.join()
    print("All tasks complete.")

Output:

Starting tasks...
Task 101: Processing 'Upload Report' for 2.5s.
Task 102: Processing 'Clear Cache' for 1.0s.
Task 102: Done.
Task 101: Done.
All tasks complete.

Common Pitfall: Mutable Default Arguments

A frequent mistake is to use a mutable object (like a list or dictionary) as a default argument in the target function. Since default arguments are created once when the function is defined, all threads will share the same object.

# --- THIS CODE IS A BUG! ---
import threading
# The list `shared_data` is created ONCE and shared by all calls.
def buggy_worker(item, shared_data=[]):
    shared_data.append(item)
    print(f"Thread {threading.current_thread().name}: Current data is {shared_data}")
if __name__ == "__main__":
    threads = []
    for i in range(5):
        thread = threading.Thread(target=buggy_worker, args=(i,))
        threads.append(thread)
        thread.start()
    for t in threads:
        t.join()
    print("\nFinal shared_data:", buggy_worker.shared_data) # Oops, this attribute doesn't exist!
    # The correct way to see the shared state is to call the function again:
    print("Final shared_data (by calling function):", buggy_worker([]))

Output (Notice the unexpected sharing):

Thread Thread-1: Current data is [0]
Thread Thread-2: Current data is [0, 1]
Thread Thread-3: Current data is [0, 1, 2]
Thread Thread-4: Current data is [0, 1, 2, 3]
Thread Thread-5: Current data is [0, 1, 2, 3, 4]
Final shared_data (by calling function): [0, 1, 2, 3, 4, []]

The Fix: Avoid mutable defaults. Pass the mutable object as an argument instead.

# --- CORRECTED CODE ---
def correct_worker(item, shared_data):
    shared_data.append(item)
    print(f"Thread {threading.current_thread().name}: Current data is {shared_data}")
if __name__ == "__main__":
    # Create one shared list to pass to all threads
    shared_list = []
    threads = []
    for i in range(5):
        thread = threading.Thread(target=correct_worker, args=(i, shared_list))
        threads.append(thread)
        thread.start()
    for t in threads:
        t.join()
    print("\nFinal shared_list:", shared_list)

Correct Output:

Thread Thread-1: Current data is [0]
Thread Thread-2: Current data is [0, 1]
Thread Thread-3: Current data is [0, 1, 2]
Thread Thread-4: Current data is [0, 1, 2, 3]
Thread Thread-5: Current data is [0, 1, 2, 3, 4]
Final shared_list: [0, 1, 2, 3, 4]
分享:
扫描分享到社交APP
上一篇
下一篇