Katkat

Implement composition and mixin utilities

Point72Data Scientist · Online Assessment

Last updated

This problem tests proficiency in functional composition, Python metaclass/decorator patterns, and custom serialization logic, focusing on handling flexible arguments and object introspection.

Problem Statement

You are building a data transformation pipeline. You need to implement two distinct utilities: a function composer and a JSON-serializable mixin.

1. Function Composer

Implement a function compose_pipeline(*funcs) that returns a new function. This returned function should accept an arbitrary number of arguments and keyword arguments, pass them to the first function in the chain, and pass the result as the sole positional argument to each subsequent function. If the input list is empty, return the identity function.

2. Serializable Mixin

Create a class JsonMixin that enables any class inheriting from it to be serialized to a JSON string.

  • Implement a method to_json(self, include_private=False).
  • It should return a JSON string of all instance attributes.
  • If include_private is False, exclude any attributes starting with an underscore _.
  • If the object contains non-serializable data (e.g., a function object or a custom class instance), catch the error and return a string literal: "[Unserializable]".

Examples

# Pipeline example
def add_five(x): return x + 5
def square(x): return x * x

pipeline = compose_pipeline(add_five, square)
print(pipeline(2)) # (2+5)^2 = 49

# Mixin example
class Data(JsonMixin):
    def __init__(self, val, secret):
        self.val = val
        self._secret = secret

d = Data(10, "hidden")
print(d.to_json(include_private=False)) # '{"val": 10}'

Constraints

  • The pipeline must handle variable arguments for the first function only.
  • Use json.dumps for the final serialization step.
  • Handle potential circular references gracefully if possible, or at least ensure the process does not crash indefinitely.

Hints

Hint 1

For the composer, use `*args` and `**kwargs` for the initial call, and then loop through the remaining functions using the result of the previous call.

Hint 2

For the mixin, use `vars(self)` or `self.__dict__` to inspect object attributes at runtime.

Hint 3

When handling non-serializable data in JSON, consider using a custom `default` parameter in `json.dumps` to replace non-serializable objects with the required string.

Follow-up questions

  • How would you modify the pipeline to support asynchronous functions if some transformations involve I/O?
  • If the object contains nested objects that also inherit from JsonMixin, how can you ensure the entire tree is serialized correctly?
  • What are the performance trade-offs of using `getattr` or `__dict__` versus using a data class or Pydantic for serialization?