Hogyan előzzük meg a névütközéseket osztályok öröklése esetén?

Tegyük fel, hogy van egy készen kapott, beimportált Számla osztályunk, amelynek csak a leírását és nyilvános attribútumait ismerjük, de a belső megvalósítását nem. Ebből az osztályból egy példányt a számlaszám és az egy alkalommal történő pénzfelvétel maximális összegének megadásával hozhatunk létre. A példányra három metódust hívhatunk meg, amelyek lehetővé teszik összeg befizetését, felvételét, valamint az egyenleg lekérdezését.

A célunk, hogy létrehozzunk egy Hitelszámla nevű osztályt a Számla osztály öröklésével. A Hitelszámla példányosításakor meg kell adnunk a számlaszámot, valamint az éves hitelkeret összegét. Mivel a Számla osztályból öröklünk, meg kell határozni a Hitelszámla inicializálásakor a hitelszámláról felvehető maximális összeget, amellyel meghívjuk a Számla osztály __init__ metódusát. Úgy döntünk, hogy az egy alkalommal felvehető összeg felső határa a hitelkeret egy hónapra eső összege lesz mindig. A Hitelszámla inicializálásakor még egy dolgot teszünk: a rendelkezésre álló hitelkeretösszeget eltároljuk egy privát változóban, hogy egy definiált metódussal ki lehessen kérni az értékét. E privát változó nevét úgy választjuk meg, hogy utaljon a tárolt mennyiség jellegére. Ezért a _limit nevet adjuk neki.

Az alábbi programsorokban láthatjuk a Hitelszámla osztály definícióját, valamint a tesztfuttatás eredményét. Megfigyelhetjük, hogy nem helyes a működés, mert hiába határoztuk meg a felvehető összeg maximumát, mégis ki lehetett venni a számláról ennél nagyobb összeget. A hiba okát elég nehéz lenne beazonosítani a szülőosztály kódjának ismerete nélkül. Ezért mutatjuk meg a Számla osztály definícióját is.

Némi vizsgálódás után rájöhetünk, hogy a hibát egy névütközés okozza. Mind a két osztályban szerepel a _limit attribútumnév, amivel az a gond, hogy a Hitelszámla osztályban felülírtuk az ősosztályból örökölt más jellegű mennyiséget tároló _limit attribútum értékét.

Az ilyen véletlen felülírás minden öröklésnél megtörténhet, ezért a Python igyekszik nyelvi szintű megoldást nyújtani minden ilyen általánosan előforduló helyzetre. A példában bemutatotthoz hasonló névütközések elkerülésére az úgynevezett névroncsolás (name mangling) mechanizmusát kínálja. Ez a furcsa elnevezés a következőt takarja.

Amennyiben egy attribútumnév a forráskódban legalább két aláhúzásjellel kezdődik, és legfeljebb egy aláhúzással végződik, akkor az interpreter az ilyen nevet a fordításkor átalakítja úgy, hogy elé helyezi az osztály nevét, amit egy darab aláhúzásjel előz meg. Az így átformált név fog az osztály vagy példány attribútumait tartalmazó __dict__ konténerbe kerülni.

Az egész eljárás célja és lényege, hogy a fordítás után már az eredeti, tehát a forráskódban szereplő attribútumnévvel az osztályon kívül nem lehet dolgozni, mert az a háttérben egy meglehetősen egyedi névvé alakult át.

Tehát, ha egy osztályt annak tudatában tervezünk, hogy abból útódosztályok fognak származni, továbbá az osztályunk olyan attribútumokkal rendelkezik, amelyeket nem szeretnénk, hogy az utódok használjanak vagy felülírjanak, akkor megfontolandó, hogy az ilyen attribútumneveket két aláhúzással kezdődjenek, mert ekkor az alosztályokban a véletlen felülírás nem történik meg.

Alább láthatjuk az ennek megfelelően átalakított két osztályt, amelyben csak annyi történt, hogy az egyetlen aláhúzással kezdődő _limit helyett a két aláhúzással kezdődő __limit nevet alkalmaztuk mindkét osztályban. A teszt eredményeiből látható, hogy most már helyesen működik a program. A legalsó kiírásban pedig láthatjuk, hogy a __limit név mivé alakult át a Hitelszámla és Számla osztályokban.

Mint általában, itt is érvényes a mondás, hogy „ingyen ebéd nincs”. Bár a módszer hasznosnak tűnik, de vannak korlátai és hátrányai. Ezekről a Python tudásépítés lépésről lépésre című e-könyv „Ne nyúlj hozzá, ha jót akarsz! – attribútumok priváttá minősítése” című fejezetében olvashatunk.

Érdekel a Python tudásépítés lépésről lépésre az alapoktól az első asztali alkalmazásig című e-könyv.