Փոփոխելի (mutable) սկզբնադիր (default) արգումենտներ

Եթե Python-ում խառնենք փոփոխելի և սկզբնադիր (default) արգումենտները, ապա սա կարող է հանգեցնել անսպասելի արդյունքների:
Եկեք սա բացատրենք պարզ օրինակով. ենթադրենք՝ դուք ուսուցիչ եք և ցանկանում եք ստեղծել մի ֆունկցիա՝ տարբեր առաջադրանքներից հետո աշակերտի միավորները գրանցելու համար: Ձեզ անհրաժեշտ է ցուցակ, որպեսզի գրանցեք միավորները և հետևեք դրանց: Այսպիսով, դուք կարող եք սկզբում գրել հետևյալը.
def record_score(new_score, score_list=[]):
    score_list.append(new_score)
    return score_list

print(record_score(95))  # [95]
print(record_score(85))  # [95, 85]
Սա կարծես թե լավ է աշխատում: record_score ֆունկցիան ընդունում է միավոր և ցուցակ: Եթե ցուցակը չի տրամադրվում, այն օգտագործում է դատարկ ցուցակ (ֆունկցիայի սահմանման մեջ score_list=[]):
Երբ կանչում եք record_score(95), թվում է, թե ֆունկցիան ճիշտ է աշխատում. այն վերադարձնում է ցուցակ 95 միավորով: Բայց երբ այնուհետև կանչում եք record_score(85), ֆունկցիան վերադարձնում է ցուցակ՝ 95-ով և 85-ով: Այնպիսի տպավորություն է, որ ֆունկցիան հիշում է վերջին անգամ կանչած ցուցակը:
Սա մտածելու տեղիք է տալիս: Ծրագրավորման լեզուներից շատերում, երբ ֆունկցիան ավարտվում է, դրա բոլոր local (տողային) փոփոխականները (ինչպես score_list-ը) ջնջվում են և մոռացվում: Հաջորդ անգամ, երբ ֆունկցիայի կանչ է լինում, այն նորից է սկսվում:
Սակայն Python-ում պատկերն այլ է այն դեպքում, երբ փոփոխական օբյեկտն օգտագործվում է որպես սկզբնադիր (default) արգումենտ։ Ծրագիրը սկզբնադիր արգումենտը ստեղծում է միայն մեկ անգամ՝ ֆունկցիան սահմանելու ժամանակ: Այսպիսով, ամեն անգամ, երբ դուք կանչում եք record_score-ն՝ առանց ցուցակ տրամադրելու, Python-ն օգտագործում է այն նույն ցուցակը, որը ստեղծել էր առաջին անգամ ֆունկցիան սահմանելիս: Եթե փոփոխում եք այդ ցուցակը (ավելացնելով տարր), փոփոխությունը պահպանվում է:
Այսպիսով, մեր ֆունկցիայում score_list-ը կիրառվում է բոլոր այն ֆունկցիայի կանչերի հետ, որտեղ մենք չենք տրամադրում մեր ցուցակը: Ահա թե ինչու մեր օրինակում միավորներն ավելանում են:
Բայց սա այն չէ, ինչ մենք ուզում էինք ստանալ: Մեր նպատակն էր, որ յուրաքանչյուր record_score-ի կանչ սկսվեր դատարկ ցուցակով, եթե մեր սեփական ցուցակը չենք տրամադրում։
Սրանից խուսափելու համար սովորաբար None-ը որպես սկզբնադիր (default) արժեք ենք կիրառում այն արգումենտների համար, որոնք կարող են փոփոխական լինել: Այնուհետև ֆունկցիայի ներսում կարող ենք ստեղծել նոր փոփոխվող օբյեկտ, եթե արգումենտը None է: Ահա թե ինչպես կարող եք գրել record_score ֆունկցիան՝ խնդրից խուսափելու համար.
def record_score(new_score, score_list=None):
    if score_list is None:  # If no list was provided,
        score_list = []     # create a new one
    score_list.append(new_score)
    return score_list

print(record_score(95))  # [95]
print(record_score(85))  # [85]
Այս շտկված տարբերակում record_score-ի յուրաքանչյուր կանչ, որը չի փոխանցում իր սեփական ցուցակը, ստանում է ամբողջովին նոր ցուցակ: Այսպիսով, տվյալները (scores) չեն կրկնվում ֆունկցիայի տարբեր կանչերի դեպքում:
💡
Ամփոփենք. Փոփոխական սկզբնադիր (default) արգումենտները Python-ում կարող են հանգեցնել անակնկալ վարքի, քանի որ դրանք ստեղծվում են միայն մեկ անգամ, և նույն օբյեկտն օգտագործվում է ամեն անգամ, երբ ֆունկցիայի կանչ է տեղի ունենում: Սակայն սա կարող է խնդիր առաջացնել, հատկապես երբ փոխում եք փոփոխվող օբյեկտը: Ուստի, սրանից խուսափելու համար կարող եք օգտագործել None-ը որպես սկզբնադիր (default) արժեք և անհրաժեշտության դեպքում ֆունկցիայում ստեղծել նոր օբյեկտ:
 
To check your solution you need to sign in
Sign in to continue