What if you could write a task once and use it everywhere without copying code?
Creating custom operators in Apache Airflow - Why You Should Know This
Imagine you have many workflows in Airflow that need to perform similar but slightly different tasks, like sending emails with different formats or processing data from various sources.
Doing this manually means copying and pasting similar code everywhere and changing small parts each time.
This manual approach is slow because you repeat the same work over and over.
It is error-prone since a small change in one place might not be updated everywhere.
Maintaining and updating these workflows becomes a headache as the number of tasks grows.
Creating custom operators lets you write the task logic once and reuse it everywhere.
You can customize behavior by passing parameters without rewriting code.
This makes your workflows cleaner, easier to maintain, and less error-prone.
def send_email_task(): # code to send email with format A def send_email_task_v2(): # similar code with slight changes for format B
from airflow.models import BaseOperator class CustomEmailOperator(BaseOperator): def __init__(self, email_format, **kwargs): super().__init__(**kwargs) self.email_format = email_format def execute(self, context): # send email using self.email_format pass
You can build flexible, reusable, and maintainable workflows that adapt easily to changing needs.
A data team creates a custom operator to load data from different databases by just changing connection details, instead of writing new code for each database.
Manual repetition causes slow and error-prone workflows.
Custom operators let you write reusable task logic once.
This improves maintainability and flexibility in Airflow pipelines.
Practice
Solution
Step 1: Understand what custom operators do
Custom operators let you create tasks that run your own code inside Airflow workflows.Step 2: Identify where custom logic is placed
This logic is placed inside theexecutemethod of the operator class.Final Answer:
To define reusable tasks with custom logic inside theexecutemethod -> Option DQuick Check:
Custom operator = reusable task logic [OK]
- Confusing UI or scheduler settings with operators
- Thinking operators install plugins
- Believing custom operators change Airflow core behavior
Solution
Step 1: Recall Python class syntax for inheritance
In Python, to inherit from a class, useclass ClassName(BaseClass):.Step 2: Match Airflow custom operator syntax
Airflow custom operators inherit fromBaseOperator, so the correct syntax isclass MyOperator(BaseOperator):.Final Answer:
class MyOperator(BaseOperator): -> Option BQuick Check:
Python class inheritance = class MyOperator(BaseOperator): [OK]
- Using function definition instead of class
- Omitting BaseOperator inheritance
- Using incorrect syntax like 'operator' keyword
from airflow.models import BaseOperator
from airflow.utils.decorators import apply_defaults
class PrintOperator(BaseOperator):
@apply_defaults
def __init__(self, message: str, **kwargs):
super().__init__(**kwargs)
self.message = message
def execute(self, context):
print(f"Message: {self.message}")
# Task instance runs with message='Hello Airflow'Solution
Step 1: Understand the execute method output
Theexecutemethod prints the string with prefix "Message:" followed by themessageattribute.Step 2: Substitute the message value
The task runs withmessage='Hello Airflow', so the print outputs "Message: Hello Airflow".Final Answer:
Message: Hello Airflow -> Option AQuick Check:
execute prints 'Message: ' + message [OK]
- Forgetting the 'Message:' prefix in print
- Confusing attribute name with string literal
- Assuming execute method is missing
from airflow.models import BaseOperator
class BadOperator(BaseOperator):
def __init__(self, param):
self.param = param
def execute(self, context):
print(self.param)Solution
Step 1: Check __init__ method for BaseOperator inheritance
When inheriting BaseOperator, you must callsuper().__init__(**kwargs)to initialize the base class properly.Step 2: Identify missing super() call
The code does not callsuper().__init__()</>, so Airflow internal setup will fail.Final Answer:
Missing call to super().__init__() in __init__ method -> Option CQuick Check:
Always call super().__init__() in custom operator init [OK]
- Omitting super().__init__() call
- Removing context parameter from execute
- Misunderstanding inheritance rules
Solution
Step 1: Check constructor and inheritance
class FilePrinterOperator(BaseOperator): @apply_defaults def __init__(self, files: list, **kwargs): super().__init__(**kwargs) self.files = files def execute(self, context): for f in self.files: print(f) correctly uses@apply_defaults, callssuper().__init__(**kwargs), and stores the list inself.files.Step 2: Verify execute method logic
class FilePrinterOperator(BaseOperator): @apply_defaults def __init__(self, files: list, **kwargs): super().__init__(**kwargs) self.files = files def execute(self, context): for f in self.files: print(f) loops overself.filesand prints each filename, matching the requirement.Final Answer:
Option A correctly implements the custom operator to print each filename -> Option AQuick Check:
Use self.files list and loop in execute() [OK]
- Not calling super().__init__()
- Using wrong parameter types
- Referencing variables without self.
- Missing context parameter in execute
Ready to prove it?
Pass the test and this pattern moves unknown
