docs
0
# List Operations
1
2
Redis list data type operations providing ordered collections of strings. Lists are implemented as linked lists, making them excellent for use cases requiring fast insertion and removal at the head and tail, such as queues, stacks, and activity feeds. Lists can hold up to 2^32 - 1 elements.
3
4
## Capabilities
5
6
### Basic List Operations
7
8
Core list manipulation functions for adding, removing, and accessing list elements.
9
10
```python { .api }
11
def lpush(self, name: KeyT, *values: EncodableT) -> ResponseT: ...
12
13
def rpush(self, name: KeyT, *values: EncodableT) -> ResponseT: ...
14
15
def lpushx(self, name: KeyT, *values: EncodableT) -> ResponseT: ...
16
17
def rpushx(self, name: KeyT, *values: EncodableT) -> ResponseT: ...
18
19
def lpop(self, name: KeyT, count: Optional[int] = None) -> Union[Optional[bytes], List[Optional[bytes]]]: ...
20
21
def rpop(self, name: KeyT, count: Optional[int] = None) -> Union[Optional[bytes], List[Optional[bytes]]]: ...
22
23
def llen(self, name: KeyT) -> ResponseT: ...
24
25
def lindex(self, name: KeyT, index: int) -> Optional[bytes]: ...
26
27
def lset(self, name: KeyT, index: int, value: EncodableT) -> ResponseT: ...
28
```
29
30
### List Range Operations
31
32
Functions for working with list ranges including retrieval, trimming, and range-based modifications.
33
34
```python { .api }
35
def lrange(self, name: KeyT, start: int, end: int) -> List[bytes]: ...
36
37
def ltrim(self, name: KeyT, start: int, end: int) -> ResponseT: ...
38
39
def lrem(self, name: KeyT, count: int, value: EncodableT) -> ResponseT: ...
40
41
def linsert(
42
self,
43
name: KeyT,
44
where: Literal["BEFORE", "AFTER"],
45
refvalue: EncodableT,
46
value: EncodableT
47
) -> ResponseT: ...
48
49
def lpos(
50
self,
51
name: KeyT,
52
value: EncodableT,
53
rank: Optional[int] = None,
54
count: Optional[int] = None,
55
maxlen: Optional[int] = None
56
) -> Union[Optional[int], List[Optional[int]]]: ...
57
```
58
59
### Blocking Operations
60
61
Blocking list operations that wait for elements to become available, useful for implementing queues and producer-consumer patterns.
62
63
```python { .api }
64
def blpop(
65
self,
66
keys: Union[KeyT, Iterable[KeyT]],
67
timeout: float = 0
68
) -> Optional[Tuple[bytes, bytes]]: ...
69
70
def brpop(
71
self,
72
keys: Union[KeyT, Iterable[KeyT]],
73
timeout: float = 0
74
) -> Optional[Tuple[bytes, bytes]]: ...
75
76
def brpoplpush(
77
self,
78
src: KeyT,
79
dst: KeyT,
80
timeout: float = 0
81
) -> Optional[bytes]: ...
82
83
def blmove(
84
self,
85
first_list: KeyT,
86
second_list: KeyT,
87
src: Literal["LEFT", "RIGHT"],
88
dest: Literal["LEFT", "RIGHT"],
89
timeout: float = 0
90
) -> Optional[bytes]: ...
91
```
92
93
### Atomic Move Operations
94
95
Functions for atomically moving elements between lists.
96
97
```python { .api }
98
def rpoplpush(self, src: KeyT, dst: KeyT) -> Optional[bytes]: ...
99
100
def lmove(
101
self,
102
first_list: KeyT,
103
second_list: KeyT,
104
src: Literal["LEFT", "RIGHT"],
105
dest: Literal["LEFT", "RIGHT"]
106
) -> Optional[bytes]: ...
107
```
108
109
## Usage Examples
110
111
### Basic List Operations
112
113
```python
114
import fakeredis
115
116
client = fakeredis.FakeRedis()
117
118
# Create a list by pushing elements
119
client.lpush('tasks', 'task1', 'task2', 'task3') # Left push (prepend)
120
client.rpush('tasks', 'task4', 'task5') # Right push (append)
121
122
# Get list length
123
length = client.llen('tasks')
124
print(f"List length: {length}") # 5
125
126
# Get entire list
127
all_tasks = client.lrange('tasks', 0, -1)
128
for task in all_tasks:
129
print(task.decode())
130
131
# Access specific elements
132
first_task = client.lindex('tasks', 0)
133
last_task = client.lindex('tasks', -1)
134
print(f"First: {first_task.decode()}, Last: {last_task.decode()}")
135
136
# Pop elements
137
left_popped = client.lpop('tasks') # Remove from left (head)
138
right_popped = client.rpop('tasks') # Remove from right (tail)
139
print(f"Popped: {left_popped.decode()}, {right_popped.decode()}")
140
```
141
142
### Queue Implementation (FIFO)
143
144
```python
145
import fakeredis
146
147
class Queue:
148
def __init__(self, client, name):
149
self.client = client
150
self.name = name
151
152
def enqueue(self, *items):
153
"""Add items to the end of the queue"""
154
return self.client.rpush(self.name, *items)
155
156
def dequeue(self):
157
"""Remove and return item from the front of the queue"""
158
item = self.client.lpop(self.name)
159
return item.decode() if item else None
160
161
def dequeue_blocking(self, timeout=0):
162
"""Block until an item is available to dequeue"""
163
result = self.client.blpop(self.name, timeout=timeout)
164
return result[1].decode() if result else None
165
166
def size(self):
167
return self.client.llen(self.name)
168
169
def peek(self, count=1):
170
"""Look at items without removing them"""
171
items = self.client.lrange(self.name, 0, count - 1)
172
return [item.decode() for item in items]
173
174
# Usage
175
client = fakeredis.FakeRedis()
176
queue = Queue(client, 'job_queue')
177
178
# Add jobs
179
queue.enqueue('process_image_1', 'send_email_2', 'backup_database')
180
181
# Process jobs
182
while queue.size() > 0:
183
job = queue.dequeue()
184
print(f"Processing: {job}")
185
```
186
187
### Stack Implementation (LIFO)
188
189
```python
190
import fakeredis
191
192
class Stack:
193
def __init__(self, client, name):
194
self.client = client
195
self.name = name
196
197
def push(self, *items):
198
"""Add items to the top of the stack"""
199
return self.client.lpush(self.name, *items)
200
201
def pop(self):
202
"""Remove and return item from the top of the stack"""
203
item = self.client.lpop(self.name)
204
return item.decode() if item else None
205
206
def peek(self):
207
"""Look at the top item without removing it"""
208
item = self.client.lindex(self.name, 0)
209
return item.decode() if item else None
210
211
def size(self):
212
return self.client.llen(self.name)
213
214
def is_empty(self):
215
return self.size() == 0
216
217
# Usage
218
client = fakeredis.FakeRedis()
219
stack = Stack(client, 'call_stack')
220
221
# Add function calls
222
stack.push('main()', 'process_data()', 'validate_input()')
223
224
# Process stack (LIFO)
225
while not stack.is_empty():
226
func = stack.pop()
227
print(f"Returning from: {func}")
228
```
229
230
### List Manipulation
231
232
```python
233
import fakeredis
234
235
client = fakeredis.FakeRedis()
236
237
# Initialize a list
238
client.rpush('numbers', '1', '2', '3', '2', '4', '2', '5')
239
240
# Find positions of elements
241
positions = client.lpos('numbers', '2', count=0) # Find all occurrences
242
print(f"Positions of '2': {positions}")
243
244
# Insert element before/after another element
245
client.linsert('numbers', 'BEFORE', '3', '2.5')
246
client.linsert('numbers', 'AFTER', '4', '4.5')
247
248
# Remove specific elements
249
removed_count = client.lrem('numbers', 2, '2') # Remove first 2 occurrences of '2'
250
print(f"Removed {removed_count} occurrences of '2'")
251
252
# Update element at specific index
253
client.lset('numbers', 0, '0') # Set first element to '0'
254
255
# Trim list to keep only a range
256
client.ltrim('numbers', 1, -2) # Keep elements from index 1 to second-to-last
257
258
# View final result
259
result = client.lrange('numbers', 0, -1)
260
print("Final list:", [item.decode() for item in result])
261
```
262
263
### Multiple List Operations
264
265
```python
266
import fakeredis
267
268
client = fakeredis.FakeRedis()
269
270
# Pop from multiple lists (first available)
271
client.rpush('list1', 'a', 'b', 'c')
272
client.rpush('list2', 'x', 'y', 'z')
273
274
result = client.blpop(['list1', 'list2'], timeout=1)
275
if result:
276
list_name, value = result
277
print(f"Popped '{value.decode()}' from {list_name.decode()}")
278
279
# Move elements between lists atomically
280
client.rpush('source', 'item1', 'item2', 'item3')
281
client.lpush('destination', 'existing')
282
283
# Move from right of source to left of destination
284
moved_item = client.rpoplpush('source', 'destination')
285
print(f"Moved item: {moved_item.decode()}")
286
287
# More flexible move operation
288
moved_item = client.lmove('source', 'destination', 'LEFT', 'RIGHT')
289
print(f"Moved item: {moved_item.decode()}")
290
```
291
292
### Conditional Push Operations
293
294
```python
295
import fakeredis
296
297
client = fakeredis.FakeRedis()
298
299
# Push only if list exists
300
result = client.lpushx('nonexistent', 'value')
301
print(f"Push to nonexistent list: {result}") # 0 (failed)
302
303
# Create the list first
304
client.lpush('existing', 'initial')
305
306
# Now pushx will work
307
result = client.lpushx('existing', 'new_item')
308
print(f"Push to existing list: {result}") # 2 (new length)
309
310
result = client.rpushx('existing', 'another_item')
311
print(f"Right push to existing list: {result}") # 3 (new length)
312
```
313
314
### Producer-Consumer Pattern
315
316
```python
317
import fakeredis
318
import threading
319
import time
320
import random
321
322
def producer(client, queue_name, producer_id):
323
"""Produce items and add them to the queue"""
324
for i in range(5):
325
item = f"item_{producer_id}_{i}"
326
client.rpush(queue_name, item)
327
print(f"Producer {producer_id} added: {item}")
328
time.sleep(random.uniform(0.1, 0.5))
329
330
def consumer(client, queue_name, consumer_id):
331
"""Consume items from the queue"""
332
while True:
333
# Block for up to 5 seconds waiting for an item
334
result = client.blpop(queue_name, timeout=5)
335
if result:
336
_, item = result
337
print(f"Consumer {consumer_id} processed: {item.decode()}")
338
time.sleep(random.uniform(0.2, 0.8)) # Simulate processing
339
else:
340
print(f"Consumer {consumer_id} timed out, stopping")
341
break
342
343
# Usage
344
client = fakeredis.FakeRedis()
345
queue_name = 'work_queue'
346
347
# Start producers and consumers
348
threads = []
349
350
# Start 2 producers
351
for i in range(2):
352
t = threading.Thread(target=producer, args=(client, queue_name, i))
353
threads.append(t)
354
t.start()
355
356
# Start 3 consumers
357
for i in range(3):
358
t = threading.Thread(target=consumer, args=(client, queue_name, i))
359
threads.append(t)
360
t.start()
361
362
# Wait for all threads to complete
363
for t in threads:
364
t.join()
365
366
print("All producers and consumers finished")
367
```
368
369
### Activity Feed Implementation
370
371
```python
372
import fakeredis
373
import time
374
import json
375
376
class ActivityFeed:
377
def __init__(self, client, user_id, max_items=100):
378
self.client = client
379
self.key = f'feed:{user_id}'
380
self.max_items = max_items
381
382
def add_activity(self, activity_type, data):
383
"""Add a new activity to the feed"""
384
activity = {
385
'type': activity_type,
386
'data': data,
387
'timestamp': int(time.time())
388
}
389
390
# Add to the left (most recent first)
391
self.client.lpush(self.key, json.dumps(activity))
392
393
# Keep only the most recent max_items
394
self.client.ltrim(self.key, 0, self.max_items - 1)
395
396
def get_activities(self, limit=10):
397
"""Get the most recent activities"""
398
activities = self.client.lrange(self.key, 0, limit - 1)
399
return [json.loads(activity.decode()) for activity in activities]
400
401
def get_activity_count(self):
402
"""Get total number of activities in feed"""
403
return self.client.llen(self.key)
404
405
# Usage
406
client = fakeredis.FakeRedis()
407
feed = ActivityFeed(client, 'user123')
408
409
# Add various activities
410
feed.add_activity('login', {'ip': '192.168.1.1'})
411
feed.add_activity('purchase', {'item': 'laptop', 'amount': 999.99})
412
feed.add_activity('comment', {'post_id': 456, 'text': 'Great post!'})
413
feed.add_activity('like', {'post_id': 789})
414
415
# Retrieve recent activities
416
recent = feed.get_activities(limit=5)
417
for activity in recent:
418
print(f"{activity['type']}: {activity['data']} at {activity['timestamp']}")
419
```
420
421
### Batch Processing with Pop Multiple
422
423
```python
424
import fakeredis
425
426
client = fakeredis.FakeRedis()
427
428
# Add batch of items to process
429
items = [f'task_{i}' for i in range(10)]
430
client.rpush('batch_queue', *items)
431
432
# Process items in batches
433
batch_size = 3
434
while client.llen('batch_queue') > 0:
435
# Pop multiple items at once (Redis 6.2+)
436
batch = client.lpop('batch_queue', count=batch_size)
437
438
if isinstance(batch, list):
439
# Multiple items returned
440
print(f"Processing batch: {[item.decode() for item in batch if item]}")
441
else:
442
# Single item or None
443
if batch:
444
print(f"Processing single item: {batch.decode()}")
445
break
446
```