Skip to content

SEMI-ADVANCED SLICE() EXAMPLES

Python
#!/usr/bin/env python3
"""
SEMI-ADVANCED SLICE() EXAMPLES
Advanced patterns, custom classes, and optimization techniques
"""

print("=" * 60)
print("SEMI-ADVANCED SLICE() - Advanced Slicing Techniques")
print("=" * 60)

# Example 1: The indices() method
print("\n1. Understanding slice.indices()")
print("-" * 40)
print("indices(len) normalizes slice to actual indices\n")

data = list(range(10))  # [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
print(f"Data: {data} (length: {len(data)})\n")

slices_to_test = [
    slice(2, 8, 2),
    slice(-5, -1),
    slice(None, None, 2),
    slice(100),  # Beyond length
    slice(-100, 100),  # Beyond both ends
]

for s in slices_to_test:
    start, stop, step = s.indices(len(data))
    result = data[s]
    print(f"slice{s.start, s.stop, s.step}:")
    print(f"  indices() returns: ({start}, {stop}, {step})")
    print(f"  Result: {result}\n")

# Example 2: Custom class with slice support
print("\n2. Custom Class with Slicing Support")
print("-" * 40)

class TimeSeriesData:
    def __init__(self, data, labels):
        self.data = data
        self.labels = labels

    def __getitem__(self, key):
        if isinstance(key, slice):
            return TimeSeriesData(
                self.data[key],
                self.labels[key]
            )
        return self.data[key]

    def __repr__(self):
        pairs = [f"{label}:{value}" for label, value in zip(self.labels, self.data)]
        return f"TimeSeriesData([{', '.join(pairs)}])"

    def __len__(self):
        return len(self.data)

# Create time series
ts = TimeSeriesData(
    data=[100, 105, 110, 108, 112, 115, 118, 120],
    labels=['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug']
)

print(f"Original: {ts}\n")
print(f"First half (slice(4)): {ts[slice(4)]}")
print(f"Every other (slice(None, None, 2)): {ts[slice(None, None, 2)]}")
print(f"Last 3 (slice(-3, None)): {ts[slice(-3, None)]}")

# Example 3: Slice composition and transformation
print("\n3. Composing and Transforming Slices")
print("-" * 40)

def combine_slices(s1, s2, length):
    """Apply s2 to the result of s1"""
    start1, stop1, step1 = s1.indices(length)
    # Length after first slice
    new_length = len(range(start1, stop1, step1))
    # Apply second slice
    start2, stop2, step2 = s2.indices(new_length)

    # Transform indices
    indices = range(start1, stop1, step1)
    sub_indices = list(indices)[start2:stop2:step2]

    return sub_indices

data = list(range(20))
print(f"Data: {data}\n")

s1 = slice(5, 15)  # Get middle section
s2 = slice(None, None, 2)  # Every other

result_indices = combine_slices(s1, s2, len(data))
print(f"First slice(5, 15): {data[s1]}")
print(f"Then slice(None, None, 2) on result: {[data[i] for i in result_indices]}")

# Example 4: Circular buffer with slicing
print("\n4. Circular Buffer Implementation")
print("-" * 40)

class CircularBuffer:
    def __init__(self, size):
        self.size = size
        self.buffer = [None] * size
        self.position = 0
        self.count = 0

    def append(self, item):
        self.buffer[self.position] = item
        self.position = (self.position + 1) % self.size
        self.count = min(self.count + 1, self.size)

    def __getitem__(self, key):
        if isinstance(key, slice):
            # Get valid indices
            start, stop, step = key.indices(self.count)
            result = []
            for i in range(start, stop, step):
                # Map logical index to buffer position
                actual_pos = (self.position - self.count + i) % self.size
                result.append(self.buffer[actual_pos])
            return result
        else:
            actual_pos = (self.position - self.count + key) % self.size
            return self.buffer[actual_pos]

    def __len__(self):
        return self.count

# Demo circular buffer
cb = CircularBuffer(5)
for i in range(8):
    cb.append(f"Item{i}")
    print(f"Added Item{i}, buffer state: {cb[slice(None)]}")

print(f"\nFinal buffer:")
print(f"  Full view: {cb[slice(None)]}")
print(f"  Last 3: {cb[slice(-3, None)]}")
print(f"  Every other: {cb[slice(None, None, 2)]}")

# Example 5: Efficient slice-based filtering
print("\n5. Slice-Based Data Filtering Pipeline")
print("-" * 40)

class DataFilter:
    def __init__(self, data):
        self.data = data
        self.slices = []

    def skip_first(self, n):
        """Skip first n elements"""
        self.slices.append(('skip_first', slice(n, None)))
        return self

    def take(self, n):
        """Take only n elements"""
        self.slices.append(('take', slice(n)))
        return self

    def every_nth(self, n):
        """Take every nth element"""
        self.slices.append(('every_nth', slice(None, None, n)))
        return self

    def execute(self):
        """Apply all slices in order"""
        result = self.data
        for name, s in self.slices:
            result = result[s]
            print(f"  After {name}: {result}")
        return result

data = list(range(1, 51))
print(f"Original data: {data[:10]}...{data[-5:]} (1-50)\n")
print("Applying filters:")

filtered = DataFilter(data).skip_first(10).every_nth(3).take(5).execute()
print(f"\nFinal result: {filtered}")

# Example 6: Slicing with assignment
print("\n6. Slice Assignment and Modification")
print("-" * 40)

data = list(range(10))
print(f"Original: {data}")

# Replace middle section
middle = slice(3, 7)
data[middle] = [99, 88, 77, 66]
print(f"Replace [3:7]: {data}")

# Insert elements (replace 0-length slice)
data = list(range(10))
insert_at = slice(5, 5)
data[insert_at] = [100, 200]
print(f"Insert at 5: {data}")

# Delete elements
data = list(range(10))
delete_range = slice(2, 5)
data[delete_range] = []
print(f"Delete [2:5]: {data}")

# Replace with different length
data = list(range(10))
replace_slice = slice(3, 6)
data[replace_slice] = [111, 222, 333, 444, 555]  # More elements
print(f"Replace with more: {data}")

# Example 7: Memory-efficient iteration with slices
print("\n7. Processing Large Data in Chunks")
print("-" * 40)

def process_in_chunks(data, chunk_size, processor):
    """Process data in chunks without creating sublists"""
    results = []
    for i in range(0, len(data), chunk_size):
        chunk_slice = slice(i, min(i + chunk_size, len(data)))
        chunk_result = processor(data[chunk_slice])
        results.append(chunk_result)
    return results

# Simulate large dataset
large_data = list(range(1, 101))

# Process in chunks of 10
chunk_sums = process_in_chunks(
    large_data,
    chunk_size=10,
    processor=sum
)

print(f"Data size: {len(large_data)} elements")
print(f"Chunk size: 10")
print(f"Chunk sums: {chunk_sums}")
print(f"Total sum: {sum(chunk_sums)}")

# Example 8: Advanced slice arithmetic
print("\n8. Slice Range Calculations")
print("-" * 40)

def slice_length(s, sequence_length):
    """Calculate how many elements a slice will return"""
    start, stop, step = s.indices(sequence_length)
    return len(range(start, stop, step))

def slices_overlap(s1, s2, sequence_length):
    """Check if two slices overlap"""
    start1, stop1, _ = s1.indices(sequence_length)
    start2, stop2, _ = s2.indices(sequence_length)

    range1 = set(range(start1, stop1))
    range2 = set(range(start2, stop2))

    return bool(range1 & range2)

length = 20
s1 = slice(5, 15)
s2 = slice(10, 18)
s3 = slice(16, 20)

print(f"Sequence length: {length}")
print(f"Slice 1: slice(5, 15)")
print(f"  Length: {slice_length(s1, length)}")

print(f"Slice 2: slice(10, 18)")
print(f"  Length: {slice_length(s2, length)}")

print(f"\nDo slice(5, 15) and slice(10, 18) overlap?")
print(f"  {slices_overlap(s1, s2, length)}")

print(f"Do slice(5, 15) and slice(16, 20) overlap?")
print(f"  {slices_overlap(s1, s3, length)}")

# Example 9: Lazy evaluation with slices
print("\n9. Lazy Slice Evaluation")
print("-" * 40)

class LazySliceView:
    """View of data that doesn't copy until needed"""
    def __init__(self, data, slice_obj=None):
        self.data = data
        self.slice_obj = slice_obj or slice(None)

    def __getitem__(self, key):
        if isinstance(key, slice):
            # Create new view by composing slices
            return LazySliceView(self.data, key)
        # Evaluate on access
        start, stop, step = self.slice_obj.indices(len(self.data))
        indices = range(start, stop, step)
        return self.data[list(indices)[key]]

    def materialize(self):
        """Convert view to actual list"""
        return self.data[self.slice_obj]

    def __repr__(self):
        return f"LazySliceView(slice{self.slice_obj.start, self.slice_obj.stop, self.slice_obj.step})"

large_data = list(range(1000))
view = LazySliceView(large_data)

print(f"Created view of {len(large_data)} elements")
print(f"View: {view}")

subset = view[100:200]
print(f"Created subset view: {subset}")

final = subset[::10]
print(f"Created final view (every 10th): {final}")

print(f"Materialized result: {final.materialize()}")

# Example 10: Slice validation and sanitization
print("\n10. Slice Validation Helper")
print("-" * 40)

def validate_and_fix_slice(s, length, name="slice"):
    """Validate slice and return normalized version"""
    issues = []

    start, stop, step = s.indices(length)

    # Check for empty result
    if start >= stop and step > 0:
        issues.append(f"Empty slice: start({start}) >= stop({stop})")
    elif start <= stop and step < 0:
        issues.append(f"Empty slice: start({start}) <= stop({stop}) with negative step")

    # Check for step=0
    if s.step == 0:
        issues.append("Invalid: step cannot be 0")

    # Report original vs normalized
    if (s.start, s.stop, s.step) != (start, stop, step):
        issues.append(f"Normalized from ({s.start}, {s.stop}, {s.step}) to ({start}, {stop}, {step})")

    if issues:
        print(f"{name}:")
        for issue in issues:
            print(f"  - {issue}")
    else:
        print(f"{name}: OK")

    return slice(start, stop, step)

test_slices = [
    (slice(5, 10), "Normal slice"),
    (slice(100, 200), "Beyond bounds"),
    (slice(-5, None), "Negative start"),
    (slice(10, 5), "Backwards (positive step)"),
    (slice(None, None, -2), "Negative step"),
]

for s, name in test_slices:
    validate_and_fix_slice(s, 20, name)

print("\n" + "=" * 60)
print("Advanced Concepts:")
print("  - slice.indices(len) normalizes to actual indices")
print("  - Implement __getitem__ for custom slicing behavior")
print("  - Slice composition for complex transformations")
print("  - Lazy evaluation avoids unnecessary copying")
print("  - Slice assignment: data[s] = new_values")
print("  - Validate slices before applying to avoid errors")
print("=" * 60)