వేరియబుల్ ఆర్గ్యుమెంట్స్ & స్కోప్ (*args, **kwargs & LEGB)

🌐 Modern Python 3.12+ 📖 Chapter 17 of 25 📁 Phase 04: ఫంక్షన్స్, మాడ్యూల్స్ & ఫైల్ I/O 🗓️ 2026 Edition
📌 Covered in this chapter: ఆర్బిట్రరీ పొజిషనల్ ఆర్గ్యుమెంట్స్ (*args) · ఆర్బిట్రరీ కీవర్డ్ ఆర్గ్యుమెంట్స్ (**kwargs) · LEGB స్కోప్ రూల్ · global & nonlocal కీవర్డ్స్ · Arbitrary Positional Arguments (*args) · Arbitrary Keyword Arguments (**kwargs) · Master Parameter Ordering Rule · Unpacking Operators (* & **) · LEGB Scope Hierarchy · global vs nonlocal Keywords · Closures · UnboundLocalError Resolution · Microservice Event Logger Project
మీరు ఒక ఫంక్షన్‌ను డిజైన్ చేస్తున్నప్పుడు, భవిష్యత్తులో దానికి యూజర్లు ఎన్ని ఆర్గ్యుమెంట్లు పంపుతారో ముందే ఖచ్చితంగా తెలియకపోతే ఎలా? ఉదాహరణకు, ఒక కాలిక్యులేటర్‌లో ఇద్దరు వ్యక్తులు ఇద్దరి మార్కులు మాత్రమే కూడవచ్చు, మరొకరు వంద మంది విద్యార్థుల మార్కులు ఒకేసారి పంపవచ్చు. లేదా ఒక క్లౌడ్ లాగింగ్ ఫంక్షన్‌కు ఒక్కో ఈవెంట్‌లో వేర్వేరు సమాచార ఫీల్డ్స్ (IP, Browser, Location, UserID, SessionID) రావచ్చు.

ఈ అపరిమిత వైవిధ్యాన్ని స్వీకరించడానికి పైథాన్ అందించిన మ్యాజికల్ ఆయుధాలే *args మరియు **kwargs! అంతేకాకుండా, ఒక ఫంక్షన్ లోపల మనం సృష్టించిన వేరియబుల్స్ ప్రోగ్రామ్‌లో ఎక్కడెక్కడ అందుబాటులో ఉంటాయి? ఫంక్షన్ బయట ఉన్న వేరియబుల్‌ను లోపల ఎలా సవరించాలి? పైథాన్ ఇంటర్ప్రెటర్ వేరియబుల్స్‌ను వెతకడానికి ఉపయోగించే ప్రసిద్ధ LEGB (Local, Enclosing, Global, Built-in) స్కోప్ ఆర్కిటెక్చర్ నియమాలు ఏమిటి? ఈ అధ్యాయంలో మనం ఈ అధునాతన ఫంక్షనల్ అంశాలన్నింటినీ విశ్లేషిస్తూ, ఎంటర్‌ప్రైజ్ మైక్రోసర్వీస్ ఈవెంట్ లాగర్ ప్రాజెక్ట్‌ను లైవ్‌గా నిర్మిద్దాం!

1 వేరియబుల్ ఆర్గ్యుమెంట్స్ ఎందుకు అవసరం? (Flexible Signatures)

సాధారణ ఫంక్షన్లలో మనం ఎన్ని పారామీటర్లను రాస్తే, కాల్ చేసేటప్పుడు సరిగ్గా అన్ని ఆర్గ్యుమెంట్లనే పంపాలి. ఒక్కటి ఎక్కువైనా లేదా తక్కువైనా పైథాన్ వెంటనే TypeError విసురుతుంది:

def add(a, b): return a + b
add(10, 20, 30) # TypeError: add() takes 2 positional arguments but 3 were given!

కానీ వాస్తవ ప్రపంచంలో ఫంక్షన్లు అత్యంత సౌకర్యవంతంగా (Flexible) ఉండాలి. ఉదాహరణకు పైథాన్ యొక్క బిల్ట్-ఇన్ print() ఫంక్షన్‌ను గమనించండి — దానికి మీరు ఒక పదం పంపినా ప్రింట్ చేస్తుంది, కామాలతో వేరుచేసి వంద పదాలు పంపినా వాటన్నింటినీ ప్రింట్ చేస్తుంది! ఈ విధమైన ఫ్లెక్సిబిలిటీని మన స్వంత ఫంక్షన్లకు అందించడానికి *args మరియు **kwargs ఉపయోగపడతాయి.

2 *args — ఆర్బిట్రరీ పొజిషనల్ ఆర్గ్యుమెంట్స్ (Tuple Packing)

ఫంక్షన్ పారామీటర్ ముందు ఒక స్టార్ సింబల్ * పెడితే, అది కాల్ చేసేటప్పుడు పంపిన అదనపు పొజిషనల్ ఆర్గ్యుమెంట్లు అన్నింటినీ ఒకే ఒక టుపుల్ (Tuple) లోకి ప్యాక్ చేస్తుంది.

3 **kwargs — ఆర్బిట్రరీ కీవర్డ్ ఆర్గ్యుమెంట్స్ (Dict Packing)

ఫంక్షన్ పారామీటర్ ముందు డబుల్ స్టార్ సింబల్ ** పెడితే, అది కాల్ చేసేటప్పుడు పంపిన అదనపు కీవర్డ్ ఆర్గ్యుమెంట్లు (Named Arguments: key=value) అన్నింటినీ ఒక డిక్షనరీ (Dictionary) లోకి ప్యాక్ చేస్తుంది.

4 ఫంక్షన్ పారామీటర్ల మాస్టర్ ఆర్డరింగ్ రూల్ (The Master Ordering Rule)

ఒకే ఫంక్షన్‌లో సాధారణ పారామీటర్లు, డిఫాల్ట్ పారామీటర్లు, *args, కీవర్డ్-ఓన్లీ, మరియు **kwargs లను కలిపి రాయాల్సి వచ్చినప్పుడు, పైథాన్ ఒక ఖచ్చితమైన వరుస క్రమాన్ని నిర్దేశిస్తుంది:

[Python Master Function Signature Order]
def function_name(
    1. standard_positional, # సాధారణ పొజిషనల్ పారామీటర్లు
    2. default_positional=val, # డిఫాల్ట్ విలువలు కలిగినవి
    3. *args, # అదనపు పొజిషనల్ టుపుల్ ప్యాకింగ్
    4. keyword_only, # స్టార్ తర్వాత వచ్చే కీవర్డ్-ఓన్లీ
    5. **kwargs # అదనపు కీవర్డ్ డిక్షనరీ ప్యాకింగ్
):

5 ఆర్గ్యుమెంట్ అన్‌ప్యాకింగ్ ట్రిక్స్ (Unpacking with * & **)

స్టార్ ఆపరేటర్ కేవలం పారామీటర్లను ప్యాక్ చేయడానికే కాకుండా, ఇప్పటికే ఉన్న లిస్ట్‌లు, టుపుల్స్ మరియు డిక్షనరీలను ఫంక్షన్ కాల్స్‌లోకి అన్‌ప్యాక్ (విడదీయడానికి) కూడా ఉపయోగపడుతుంది:

6 కోడ్ డెమో 1: డైనమిక్ API రిక్వెస్ట్ బిల్డర్ with *args & **kwargs

ఎన్ని పాత్ పారామీటర్లు మరియు క్వెరీ పారామీటర్లు వచ్చినా డైనమిక్‌గా సరైన API URL ను నిర్మించే ఫంక్షన్‌ను పరిశీలిద్దాం:

01_dynamic_api_url_builder.py
# =====================================================================
# చాప్టర్ 17 డెమో 1: *args & **kwargs డైనమిక్ API URL బిల్డర్
# =====================================================================

def build_api_endpoint(base_domain: str, *path_segments, protocol: str = "https", **query_params) -> str:
    """డైనమిక్ పాత్‌లు మరియు క్వెరీ పారామీటర్లతో API ఎండ్‌పాయింట్‌ను నిర్మిస్తుంది."""
    # 1. *args లోని అన్ని సెగ్మెంట్లను / తో జాయిన్ చేయడం
    clean_paths = [str(seg).strip("/") for seg in path_segments if seg]
    path_url = "/".join(clean_paths)

    # 2. **kwargs లోని క్వెరీ పారామీటర్లను key=value గా మార్చడం
    query_parts = [f"{k}={v}" for k, v in query_params.items()]
    query_string = ("?" + "&".join(query_parts)) if query_parts else ""

    full_url = f"{protocol}://{base_domain}/{path_url}{query_string}"
    return full_url

# టెస్ట్ 1: సాధారణ యూజర్ పాత్ & ఫిల్టర్లు
url_1 = build_api_endpoint(
    "api.ourcompiler.com",
    "v1", "users", 1042, "orders",
    protocol="https",
    status="delivered",
    page=1,
    limit=20
)
print("ఎండ్‌పాయింట్ 1 :", url_1)

# టెస్ట్ 2: ఆర్గ్యుమెంట్ అన్‌ప్యాకింగ్ (*paths, **filters)
extra_paths = ["v2", "products", "laptops"]
extra_filters = {"brand": "asus", "min_ram_gb": 16}

url_2 = build_api_endpoint("api.ourcompiler.com", *extra_paths, **extra_filters)
print("ఎండ్‌పాయింట్ 2 :", url_2)

7 వేరియబుల్ స్కోప్ అంటే ఏమిటి? (Variable Scope & Lifetime)

పైథాన్‌లో ఒక వేరియబుల్‌ను డిక్లేర్ చేసినప్పుడు, అది ప్రోగ్రామ్‌లోని ఏ ఏ ప్రదేశాలలో అందుబాటులో ఉంటుంది (Visibility) మరియు మెమరీలో ఎంతకాలం జీవించి ఉంటుంది (Lifetime) అనే దానిని నిర్ణయించే నియమ నిబంధనలనే స్కోప్ (Scope) అంటారు.

8 The LEGB స్కోప్ రూల్ డీప్ డైవ్ (Local, Enclosing, Global, Built-in)

పైథాన్‌లో మీరు ఏదైనా ఒక వేరియబుల్ పేరును ఉపయోగించినప్పుడు, ఇంటర్ప్రెటర్ ఆ పేరును ఎలా వెతుకుతుంది? దీనికి పైథాన్ ఒక ఖచ్చితమైన 4-దశల క్రమాన్ని పాటిస్తుంది — దీనినే LEGB Rule అంటారు:

[The LEGB Scope Lookup Order]
1. [L] Local ─────▶ ప్రస్తుత ఫంక్షన్ బాడీలో ఉందా? (Found? Stop Search!)
                            │ లేకపోతే
2. [E] Enclosing ─▶ బయటి చుట్టుముట్టిన ఫంక్షన్ (Outer Nested Function) లో ఉందా?
                            │ లేకపోతే
3. [G] Global ────▶ ప్రస్తుత మాడ్యూల్ లేదా మెయిన్ ఫైల్ లెవల్‌లో ఉందా?
                            │ లేకపోతే
4. [B] Built-in ──▶ పైథాన్ ఇన్-బిల్ట్ ఫంక్షన్స్ (len, print, range) లో ఉందా?
                            │ ఎక్కడా దొరక్కపోతే
❌ NameError: name 'x' is not defined!

బైట్‌కోడ్ స్థాయిలో LEGB అంతర్గత విశ్లేషణ:

9 global కీవర్డ్ — గ్లోబల్ స్టేట్ మోడిఫికేషన్స్ & ప్రమాదాలు

ఒక ఫంక్షన్ లోపల ఉంటూ గ్లోబల్ వేరియబుల్‌లోని విలువను చదవడం సులభమే. కానీ దానిని సవరించాలని (Modify) చూసినప్పుడు, పైథాన్ ఆ పేరుతో ఒక సరికొత్త లోకల్ వేరియబుల్‌ను సృష్టిస్తుంది తప్ప గ్లోబల్ వేరియబుల్‌ను మార్చదు!

ఫంక్షన్ లోపల నుండే అసలైన గ్లోబల్ వేరియబుల్‌ను మార్చాలంటే global కీవర్డ్‌ను ఉపయోగించాలి:

total_visitors = 0 # Global Scope

def track_visitor():
    global total_visitors # గ్లోబల్ వేరియబుల్ వాడుతున్నామని తెలియజేయడం
    total_visitors += 1

track_visitor()
print("మొత్తం సందర్శకులు :", total_visitors) # 1
ఇండస్ట్రీ బెస్ట్ ప్రాక్టీస్ హెచ్చరిక: గ్లోబల్ వేరియబుల్స్‌ను మార్చడానికి global కీవర్డ్ అతిగా వాడటం వల్ల మల్టీ-థ్రెడింగ్ అప్లికేషన్లలో రేస్ కండిషన్లు (Race Conditions) మరియు అనూహ్యమైన బగ్‌లు వస్తాయి. వీలైనంతవరకు ఫంక్షన్ల ద్వారా విలువలను రిటర్న్ చేయడానికే ప్రాధాన్యత ఇవ్వండి.

10 nonlocal కీవర్డ్ — నెస్టెడ్ ఫంక్షన్స్ & Closures ఆర్కిటెక్చర్

ఒక ఫంక్షన్ లోపల మరొక ఫంక్షన్‌ను రాసినప్పుడు (Nested Functions), లోపలి ఫంక్షన్ తన బయట ఉన్న ఫంక్షన్‌లోని వేరియబుల్‌ను సవరించడానికి nonlocal కీవర్డ్‌ను ఉపయోగిస్తుంది.

క్లోజర్ (Closure) అంటే ఏమిటి?
ఒక ఇన్నర్ ఫంక్షన్ తన బయటి ఫంక్షన్ ఎగ్జిక్యూషన్ ముగిసిపోయినప్పటికీ, బయటి ఫంక్షన్ లోని వేరియబుల్స్ (Enclosing Scope State) ను గుర్తుపెట్టుకుని ఉండగలిగితే దానిని క్లోజర్ (Closure) అంటారు.

11 కోడ్ డెమో 2: క్లోజర్-బేస్డ్ బ్యాంక్ వాలెట్ with nonlocal

nonlocal కీవర్డ్ ద్వారా ఒక ప్రైవేట్ బ్యాలెన్స్‌ను భద్రపరిచే క్లోజర్ ఇంప్లిమెంటేషన్‌ను క్రింద పరిశీలిద్దాం:

02_closure_wallet_nonlocal.py
# nonlocal & క్లోజర్ డెమో
def make_secure_wallet(initial_balance: float):
    """ఒక ప్రైవేట్ బ్యాలెన్స్ స్టేట్ కలిగిన వాలెట్ ఫంక్షన్‌ను సృష్టిస్తుంది."""
    balance = initial_balance  # Enclosing Scope Variable

    def spend(amount: float):
        nonlocal balance  # బయటి ఫంక్షన్ లోని balance ను మార్చడానికి అనుమతి!
        if amount > balance:
            return f"❌ లావాదేవీ విఫలం! నిల్వ సరిపోదు (ప్రస్తుతం: ₹{balance:,.2f})"
        balance -= amount
        return f"✅ ₹{amount:,.2f} చెల్లించబడింది. మిగిలిన బ్యాలెన్స్: ₹{balance:,.2f}"

    return spend

# వాలెట్ సృష్టి (రాహుల్ వాలెట్ ప్రారంభ బ్యాలెన్స్ ₹5,000)
rahul_wallet = make_secure_wallet(5000.0)

print(rahul_wallet(1200)) # 5000 - 1200 = 3800
print(rahul_wallet(2500)) # 3800 - 2500 = 1300
print(rahul_wallet(2000)) # ఫెయిల్! (1300 మాత్రమే ఉంది)

12 కామన్ బిగినర్స్ మిస్టేక్స్ & UnboundLocalError బగ్

13 రియల్-వరల్డ్ ప్రాజెక్ట్: "Enterprise Microservice Event Logger & Auditing Dispatcher"

ఈ పదిహేడవ అధ్యాయంలో నేర్చుకున్న అన్ని అంశాలను (*args, **kwargs, అన్‌ప్యాకింగ్, LEGB స్కోప్, క్లోజర్స్, గ్లోబల్ ఆడిట్ కౌంటర్స్) సమగ్రంగా అనుసంధానిస్తూ — వేలాది మైక్రోసర్వీసుల నుండి వచ్చే విభిన్న పేలోడ్లను ప్రాసెస్ చేసే పూర్తి స్థాయి Enterprise Event Logger & Auditing Dispatcher Engine ను నిర్మిద్దాం!

03_microservice_event_dispatcher.py
# =====================================================================
# చాప్టర్ 17 మినీ ప్రాజెక్ట్: Microservice Event Logger & Audit Dispatcher
# కాన్సెప్ట్స్: *args (Dynamic Tags), **kwargs (Flexible Event Payload),
#             LEGB Scope Resolution, Closure Metrics Tracker, Unpacking
# =====================================================================
from datetime import datetime

# Global Audit Registry (LEGB - Global Scope)
TOTAL_EVENTS_DISPATCHED = 0
SEVERITY_COUNTS = {"INFO": 0, "WARNING": 0, "CRITICAL": 0}

def create_service_event_dispatcher(service_name: str, environment: str = "PRODUCTION"):
    """ఒక నిర్దిష్ట మైక్రోసర్వీస్ కోసం క్లోజర్-ఆధారిత ఈవెంట్ డిస్పాచర్‌ను సృష్టిస్తుంది."""
    local_event_counter = 0  # Enclosing Scope Variable

    def log_event(severity: str, event_action: str, *tags, **metadata):
        nonlocal local_event_counter
        global TOTAL_EVENTS_DISPATCHED, SEVERITY_COUNTS

        local_event_counter += 1
        TOTAL_EVENTS_DISPATCHED += 1
        SEVERITY_COUNTS[severity] = SEVERITY_COUNTS.get(severity, 0) + 1

        timestamp = datetime.now().strftime("%H:%M:%S")
        tag_str = " ".join([f"#{t}" for t in tags]) if tags else "#UNTYPED"

        # కన్సోల్ ఆడిట్ అవుట్‌పుట్
        icon = "🔴" if severity == "CRITICAL" else ("🟡" if severity == "WARNING" else "🟢")
        print(f"[{timestamp}] {icon} [{severity:<8}] [{service_name}] #{local_event_counter}")
        print(f"  యాక్షన్: {event_action:<28} ట్యాగ్స్: {tag_str}")

        if metadata:
            print("  మెటాడేటా పేలోడ్:")
            for k, v in metadata.items():
                print(f"    • {k:<15} : {v}")
        print("-" * 65)

    return log_event

# 1. రెండు వేర్వేరు మైక్రోసర్వీసుల కోసం డిస్పాచర్లను సృష్టించడం
auth_service_logger = create_service_event_dispatcher("AUTH-SERVICE")
payment_service_logger = create_service_event_dispatcher("PAYMENT-SERVICE")

print("\n" + "=" * 65)
print(f"{'🌐 మన ఎంటర్‌ప్రైజ్ మైక్రోసర్వీస్ ఈవెంట్ ఆడిట్ స్ట్రీమ్ 2026':^65}")
print("=" * 65)

# 2. AUTH SERVICE లాగ్స్ (*args ద్వారా ట్యాగ్స్, **kwargs ద్వారా మెటాడేటా)
auth_service_logger(
    "INFO", "USER_LOGIN_SUCCESS",
    "security", "oauth2",
    user_id="USR-9921", ip="192.168.1.100", browser="Chrome 122"
)

auth_service_logger(
    "WARNING", "FAILED_PASSWORD_ATTEMPT",
    "bruteforce_risk", "security",
    user_id="USR-8810", ip="45.154.255.88", attempts_count=3
)

# 3. PAYMENT SERVICE లాగ్స్ (డైనమిక్ పేమెంట్ ఫీల్డ్స్)
payment_payload = {
    "txn_id": "TXN-9008129",
    "amount_inr": 24999.0,
    "gateway": "RAZORPAY",
    "card_network": "VISA"
}
payment_service_logger("INFO", "PAYMENT_CAPTURED", "finance", "instant", **payment_payload)

# 4. CRITICAL EVENT
payment_service_logger(
    "CRITICAL", "GATEWAY_TIMEOUT",
    "critical", "outage",
    gateway="RAZORPAY", error_code="ERR_504_TIMEOUT", retry_attempt=3
)

# 5. గ్లోబల్ మెట్రిక్స్ సమ్మరీ (Global Scope Audit)
print("=" * 65)
print(f"మొత్తం డిస్పాచ్ అయిన ఈవెంట్లు : {TOTAL_EVENTS_DISPATCHED}")
print(f"సెక్యూరిటీ తీవ్రత సమ్మరీ     : {SEVERITY_COUNTS}")
print("=" * 65 + "\n")

★ ఈ అధ్యాయంపై ప్రాక్టీస్ క్విజ్ (Knowledge Check)

1. *args మరియు **kwargs డేటాను ఏ డేటా స్ట్రక్చర్లలోకి ప్యాక్ చేస్తాయి?
సమాధానం: *args అదనపు పొజిషనల్ ఆర్గ్యుమెంట్లను ఒక టుపుల్ (Tuple) లోకి ప్యాక్ చేస్తుంది. **kwargs అదనపు కీవర్డ్ ఆర్గ్యుమెంట్లను ఒక డిక్షనరీ (Dictionary) లోకి ప్యాక్ చేస్తుంది.
2. LEGB రూల్ ప్రకారం పైథాన్ వేరియబుల్స్‌ను ఏ క్రమంలో వెతుకుతుంది?
సమాధానం: 1. Local (L) — ప్రస్తుత ఫంక్షన్ బాడీ. 2. Enclosing (E) — బయటి నెస్టెడ్ ఫంక్షన్ స్కోప్. 3. Global (G) — ప్రస్తుత మాడ్యూల్ లేదా మెయిన్ ఫైల్. 4. Built-in (B) — పైథాన్ అంతర్నిర్మిత నేమ్‌స్పేస్.
3. global మరియు nonlocal కీవర్డ్స్ మధ్య ముఖ్యమైన తేడా ఏమిటి?
సమాధానం: global అనేది మాడ్యూల్ స్థాయి గ్లోబల్ వేరియబుల్‌ను ఫంక్షన్ లోపల నుండి సవరించడానికి అనుమతిస్తుంది. nonlocal అనేది నెస్టెడ్ ఫంక్షన్లలో బయటి ఫంక్షన్ (Enclosing Scope) లోని వేరియబుల్‌ను సవరించడానికి ఉపయోగపడుతుంది (ఇది గ్లోబల్ వేరియబుల్స్ వరకు వెళ్ళదు).
4. UnboundLocalError ఎప్పుడు సంభవిస్తుంది? దానిని ఎలా నివారించాలి?
సమాధానం: ఒక ఫంక్షన్ లోపల వేరియబుల్‌కు విలువను అసైన్ చేయడానికి ముందే దానిని చదవడానికి (print చేయడానికి) ప్రయత్నించినప్పుడు వస్తుంది. ఆ వేరియబుల్ గ్లోబల్ స్కోప్‌లో ఉన్నప్పటికీ, ఫంక్షన్ లోపల అసైన్‌మెంట్ ఉన్నందున పైథాన్ దాన్ని లోకల్ అనుకుంటుంది. దీనిని నివారించడానికి ఫంక్షన్ ప్రారంభంలో global var_name అని రాయాలి.
5. ఫంక్షన్ సిగ్నేచర్‌లో **kwargs ఎక్కడ ఉండాలి?
సమాధానం: **kwargs తప్పనిసరిగా ఫంక్షన్ పారామీటర్ల జాబితాలో చిట్టచివరి పారామీటర్‌గా మాత్రమే ఉండాలి. దాని తర్వాత ఏ ఇతర పారామీటర్లను రాయడం సింటాక్స్ ఎర్రర్ అవుతుంది.
← మునుపటి అధ్యాయం: 16. ఫంక్షన్స్ ఫండమెంటల్స్ తర్వాతి అధ్యాయం: 18. ల్యాంబ్డా, రికర్షన్ & ఫంక్షనల్ ప్రోగ్రామింగ్ →

ఈ Microservice Event Logger ని లైవ్‌గా మన కంపైలర్‌లో రన్ చేయండి!

ఎటువంటి లోకల్ సెటప్ లేకుండా, బ్రౌజర్‌లోనే Modern Python 3 కంపైలర్‌లో ఈ కోడ్‌ను ఎగ్జిక్యూట్ చేసి అవుట్‌పుట్ తనిఖీ చేయండి.