Python Functions: Scope & Lifetime - සිංහලෙන් දැනගමු!

ආයුබෝවන්, මගේ කෝඩින් යාළුවනේ!
දැන් කාලෙ හැටියට Software Engineering කියන්නෙ සුපිරි field එකක්නෙ. කෝඩින් කරන අපි හැමෝටම සමහර වෙලාවට පොඩි පොඩි පැටලෙන තැන් එනවා. අන්න එහෙම එක තැනක් තමයි functions වල 'scope' එකයි, 'lifetime' එකයි කියන දේවල්. මේවා හරියට තේරුම් ගත්තෙ නැත්තන් වැඩේ අවුල් වෙන්න පුළුවන්, bugs එන්න පුළුවන්. ඒත් බයවෙන්න එපා, අද අපි මේක සරලව, අපේ සිංහලෙන්ම කතා කරමු, ඔයාලට කෝඩ් කරනකොට පිහිට වෙන්න.
අද අපි බලමු variable එකක scope එක කියන්නෙ මොකක්ද? ඒක local ද, global ද කියල කොහොමද දැනගන්නෙ? global
keyword එකෙන් මොනවද කරන්න පුළුවන්? ඒ වගේම, variable එකක lifetime එක කියන්නෙ මොකක්ද? ඒක කොච්චර වෙලා තියෙනවද? අවසානෙට, nested functions කියන්නෙ මොනවද කියලත් බලමු. මේ හැමදේම practical උදාහරණ එක්ක බලල, ඔයාලට පුළුවන් විදියට හොඳටම තේරුම් ගන්න උත්සාහ කරමු.
එහෙනම්, අපි පටන් ගමු නේද?
මොකක්ද මේ Scope එක?
සරලවම කිව්වොත්, scope එක කියන්නෙ variable එකක් අපිට පාවිච්චි කරන්න පුළුවන් කෝඩ් එකේ කොටස.
Local Scope (දේශීය සීමාව)
Function එකක් ඇතුළෙ declare කරන variables තමයි local variables. මේවා function එකෙන් පිටතින් access කරන්න බෑ. සරල උදාහරණයක් විදියට ගත්තොත්, ඔයාලගේ ගෙදර කුස්සියේ තියෙන බඩු කුස්සිය ඇතුළෙ විතරයි පාවිච්චි කරන්න පුළුවන් වගේ දෙයක් තමයි මේකෙන් කියවෙන්නේ. කුස්සියෙන් එලියට ගත්තොත් ඒ බඩු ටික පාවිච්චි කරන්න බැහැ වගේ, මේ local variables වලටත් ඒ අදාල function එකෙන් එලියට ගියාම අදාළත්වයක් නැහැ.
def my_function():
x = 10 # This is a local variable
print(f"Inside the function, x is: {x}")
my_function()
# print(x) # This will cause an error: NameError: name 'x' is not defined
ඉහත කෝඩ් එකේ, x
කියන variable එක my_function()
එක ඇතුළෙ විතරයි වැඩ කරන්නෙ. ඒකෙන් පිටත x
එක print
කරන්න හැදුවොත් Error එකක් එනවා, මොකද x
කියන variable එක local scope එකට අයිති නිසා.
Global Scope (ගෝලීය සීමාව)
Function එකකින් පිටත, program එකේ top-level එකේ declare කරන variables තමයි global variables. මේවා program එකේ ඕනෑම තැනක ඉඳන් access කරන්න පුළුවන්. මේක හරියට ගෙදර සාලේ තියෙන TV එක ගෙදර ඕනෑම තැනක ඉඳන් බලන්න පුළුවන් වගේ දෙයක්. ඒ කියන්නෙ, ඕනෑම තැනක සිට මේ variables වලට පිවිසෙන්න පුළුවන්.
y = 20 # This is a global variable
def another_function():
print(f"Inside another_function, y is: {y}")
def third_function():
print(f"Inside third_function, y is: {y}")
another_function()
third_function()
print(f"Outside functions, y is: {y}")
දැක්කනේ, y
කියන global variable එක another_function()
, third_function()
කියන functions දෙක ඇතුළෙදිත්, program එකේ එලියෙදිත් access කරන්න පුළුවන්.
ඔයාලට දැන් local vs global කියන එකේ වෙනස තේරෙනවා ඇති නේද?
global Keyword එකේ බලය
සාමාන්යයෙන්, function එකක් ඇතුළෙදි global variable එකක් modify කරන්න බෑ, ඒක read කරන්න විතරයි පුළුවන්. ඒත් අපිට ඕන නම් function එකක් ඇතුළෙ ඉඳන් global variable එකක value එක වෙනස් කරන්න, එතකොට අපිට global
කියන keyword එක පාවිච්චි කරන්න වෙනවා. මෙහෙම කරද්දි පොඩ්ඩක් පරිස්සම් වෙන්න ඕනේ, මොකද මේකෙන් program එකේ side effects එන්න පුළුවන්, ඒ කියන්නේ නොහිතන විදියට වෙනස්කම් වෙන්න පුළුවන්.
global_counter = 0
def increment_counter():
# print(f"Inside function before global keyword, global_counter is: {global_counter}")
# The line above would work if we were just reading. But if we try to modify directly:
# global_counter += 1 # This would create a new LOCAL variable named global_counter
print(f"Current global_counter before modification: {global_counter}")
global global_counter # Declare that we want to modify the global variable
global_counter += 1
print(f"Current global_counter after modification: {global_counter}")
print(f"Initial global_counter: {global_counter}") # 0
increment_counter()
print(f"After first call, global_counter: {global_counter}") # 1
increment_counter()
print(f"After second call, global_counter: {global_counter}") # 2
දැක්කනේ global
keyword එක කොච්චර බලවත්ද කියලා? ඒක පාවිච්චි කරද්දි දෙපාරක් හිතල පාවිච්චි කරන්න ඕනේ. මොකද මේකෙන් program එකේ behavior එකට ලොකු බලපෑමක් වෙන්න පුළුවන්.
Variable Lifetime: ඉපදීමයි මරණයයි
Variable එකක lifetime එක කියන්නෙ ඒ variable එක memory එකේ කොච්චර වෙලා ජීවත් වෙනවද කියන එක. මේක memory management වලටත් program එකේ කාර්යක්ෂමතාවයටත් (efficiency) ගොඩක් වැදගත්.
Local Variables වල Lifetime එක
- function එක call කරපු වෙලාවට තමයි මේවා "ඉපදෙන්නේ" (memory එකට එන්නේ).
- function එක execute වෙලා ඉවර වුණාම, return කරාම, මේවා "මියයනවා" (memory එකෙන් අයින් වෙනවා).
- මේක නිසා තමයි function එකෙන් එලියෙන් local variables access කරන්න බැරි. function එක ඇතුළෙ variable එක හිටියට, ඒකෙන් එලියට ආවම ඒක නෑ.
def create_and_destroy():
temp_var = 50 # temp_var 'born' here
print(f"Inside function, temp_var: {temp_var}")
# temp_var 'dies' when function finishes
create_and_destroy()
# print(temp_var) # Error: temp_var no longer exists
Global Variables වල Lifetime එක
- මේවා program එක පටන් ගන්නකොටම "ඉපදෙනවා".
- program එක execute වෙලා ඉවර වෙනකම්ම memory එකේ තියෙනවා.
- ඒක නිසා තමයි ඕනෑම තැනක ඉඳන් access කරන්න පුලුවන්. program එක දුවනකම් මේ variables ජීවමානයි.
application_name = "My Awesome App" # global_variable 'born' when program starts
def display_name():
print(f"Application Name: {application_name}")
display_name()
print(f"Accessing global outside: {application_name}")
# application_name 'dies' only when the program terminates
මේ lifetime එක තේරුම් ගන්න එක memory optimization සහ bug prevention වලට ගොඩක් වැදගත්.
Nested Functions: ගූඩු වෙච්ච Functions
Function එකක් ඇතුළෙ තවත් function එකක් define කරන එකට තමයි nested function කියන්නෙ. මේවා සමහර වෙලාවට Python වලදී closures කියලා හඳුන්වන අවස්ථාවලටත් අදාළ වෙනවා. විශේෂත්වය තමයි, inner function එකට outer function එකේ variables access කරන්න පුළුවන් වීම.
def outer_function(text):
message = text # outer function's local variable
print(f"Outer function received: {message}")
def inner_function():
print(f"From inner function: {message} (accessed from outer)") # Accessing outer function's variable
inner_function() # Calling the inner function
outer_function("Hello from Outer Function!")
# inner_function() # This will cause an error: NameError: name 'inner_function' is not defined
මේ උදාහරණයේදී inner_function()
එක outer_function()
එක ඇතුළෙ define කරලා තියෙන්නේ. inner_function()
එකට message
කියන outer_function()
එකේ local variable එක access කරන්න පුළුවන් වෙන්නේ මේ nesting එක නිසා තමයි. මේ concept එක closures, decorators වගේ advanced Python topics වලට පදනම දානවා.
මෙහෙම nested functions පාවිච්චි කරන එකෙන් code එක organize කරගන්න පුළුවන්, සමහර වෙලාවට data encapsulation කරන්නත් උදව් වෙනවා. ඒ වගේම, function එකක් තවත් function එකක helper function එකක් විදියට පාවිච්චි කරන්නත් පුළුවන්.
ප්රායෝගික උදාහරණ සහ වැරදි මඟහරවා ගනිමු!
දැන් අපි බලමු මේ scope සහ lifetime concepts කොහොමද actual project එකකදී බලපාන්නෙ කියල. ඒ වගේම, වැරදි සිදුවීම් වළක්වා ගැනීමට අනුගමනය කළ යුතු හොඳම පුරුදු (Best Practices) මොනවාද කියාත් බලමු.
Unintended Side Effects (නොහිතන විදියට වෙනස්කම්)
Global variables ඕනෑවට වඩා පාවිච්චි කරනකොට, program එකේ ඕනෑම තැනකින් ඒවා modify කරන්න පුළුවන්. ඒක නිසා අපි නොහිතන විදියට variable එකක value එක වෙනස් වෙන්න පුළුවන්. මේකෙන් bugs හොයාගන්න අමාරු වෙනවා, code එක තේරුම් ගන්නත් අමාරුයි.
# නරක පුරුද්දකට උදාහරණයක් (Example of a bad practice)
total_price = 100 # Global variable
def calculate_discount(amount):
# Here, we intend to modify the global total_price directly.
# This can lead to unexpected changes in the global state.
global total_price # Declaring intent to modify global variable
total_price -= amount * 0.1
print(f"Discount calculated. New total_price (global) is: {total_price}")
def process_order():
# This function relies on total_price, which might have been changed by calculate_discount
print(f"Processing order with current total_price: {total_price}")
print(f"Initial total_price: {total_price}") # Output: 100
calculate_discount(50) # This modifies the global total_price
# Output: Discount calculated. New total_price (global) is: 95.0
process_order() # This function now sees the modified total_price (95.0)
# Output: Processing order with current total_price: 95.0
print(f"Final total_price after everything: {total_price}") # Output: 95.0
මෙතැනදී calculate_discount
function එක total_price
කියන global variable එක modify කරනවා. ඊට පස්සෙ process_order
function එක total_price
access කරද්දි, ඒකෙ value එක වෙනස් වෙලා තියෙන්නේ. මේක program එකක් සංකීර්ණ වෙද්දි ලොකු ගැටලුවක් වෙන්න පුළුවන්, මොකද variable එකක value එක කීයටද තියෙන්නේ කියලා predict කරන්න අමාරුයි.
Best Practices (හොඳම පුරුදු)
මේ වගේ ගැටලු මඟහරවාගෙන, clean සහ maintainable code ලියන්න අපිට පුළුවන් හොඳ පුරුදු කීපයක් තියෙනවා:
- Minimize Global Variables: (Global Variables අවම කරන්න)පුළුවන් තරම් global variables පාවිච්චි කරන එක අඩු කරන්න. මේකෙන් code එක readability වැඩි වෙනවා, bugs එන එක අඩු වෙනවා, code එක maintain කරන්න ලේසියි. Data encapsulation කියලා කියන්නෙත් මේ වගේ concept එකකට. ඒ කියන්නෙ, data එකක් ඒකට අදාල functions ඇතුලෙම තියාගන්න එක.
- Use Classes/Objects for State: (තොරතුරු ගබඩා කිරීමට Classes/Objects භාවිත කරන්න)සමහර වෙලාවට data ගොඩක් තැන් වලට share කරන්න ඕන නම්, class එකක් හදාගෙන ඒක ඇතුළෙ variables (attributes) define කරන්න පුළුවන්. ඒක object-oriented programming (OOP) වල වැදගත් concept එකක්. මේකෙන් data එක organize කරගන්නත්, access control කරන්නත් ලේසියි.
- Clear Naming Conventions: (පැහැදිලි නම් යෙදීම්)Variables වලට තේරුම් ගන්න පුළුවන් නම් දෙන්න. Global variables නම් විශේෂයෙන්ම පැහැදිලිව නම් කරන්න. (e.g.,
G_MAX_USERS
,g_config_data
). මේක code එකේ readability වැඩි කරනවා.
Pass Arguments and Return Values: (Arguments යවන්න සහ Values Return කරන්න)function එකකට data ඕන නම්, ඒක argument එකක් විදියට pass කරන්න. function එකකින් data modify කරා නම්, අලුත් value එක return කරන්න. මේක තමයි functions පාවිච්චි කරන නිවැරදිම සහ නිර්දේශිත ක්රමය.
# හොඳම පුරුද්දකට උදාහරණයක් (Example of a good practice)
initial_price = 100 # This can be a variable in the main flow or passed from elsewhere
def calculate_discount_better(current_price, discount_percentage):
discount_amount = current_price * discount_percentage
new_price = current_price - discount_amount
print(f"Discount calculated. New price (local to function) is: {new_price}")
return new_price # Return the modified value, don't modify global directly
def process_order_better(price_to_process):
print(f"Processing order with current price: {price_to_process}")
print(f"Initial price: {initial_price}") # Output: 100
# Pass initial_price as an argument and receive the new value
final_price = calculate_discount_better(initial_price, 0.1)
# Output: Discount calculated. New price (local to function) is: 90.0
process_order_better(final_price) # Pass the new value to the next function
# Output: Processing order with current price: 90.0
print(f"Initial price after all operations (unchanged): {initial_price}") # Output: 100
print(f"Final price for order: {final_price}") # Output: 90.0
මේ example එකේදී initial_price
කියන variable එක calculate_discount_better
function එකෙන් කෙලින්ම modify වෙන්නෙ නෑ. ඒ වෙනුවට function එක new_price
එකක් return කරනවා. මේකෙන් code එකේ හැසිරීම (behavior) predict කරන්න ලේසියි.
අවසාන වදන
හරි යාළුවනේ, ඔයාලට දැන් Functions වල Scope එකයි Lifetime එකයි ගැන හොඳ අවබෝධයක් ලැබෙන්න ඇති කියලා මම හිතනවා. මේ Concepts ටික තේරුම් ගන්න එකෙන් ඔයාලගේ coding skills ගොඩක් දියුණු වෙනවා විතරක් නෙවෙයි, clean code ලියන්නත් පුළුවන් වෙනවා.
මතක තියාගන්න, global variables කියන්නෙ නරක දෙයක් නෙවෙයි, ඒත් ඒවා ප්රවේශමෙන්, අවශ්ය තැනට විතරක් පාවිච්චි කරන්න පුරුදු වෙන්න. Local variables වලට නිතරම priority එක දෙන්න.
අද කතා කරපු දේවල් ඔයාලගේ computer එකේම code කරලා බලන්න. එතකොට තව හොඳට තේරෙයි.
මේ ගැන ඔයාලට තව මොනවා හරි දැනගන්න තියෙනවා නම්, හරි ප්රශ්න තියෙනවා නම්, පහලින් comment එකක් දාගෙන යන්න. මම පුළුවන් විදියට උදව් කරන්නම්.
තවත් මේ වගේ වැදගත් article එකකින් හම්බෙමු!
සතුටින් කෝඩ් කරමු!