Zatím mám v plánu pouze už jen jedno další pokračování (asi tak za měsíc), kde bych napsal něco o objektech. Nerad bych se totiž dostal k tématům, kterým moc nerozumím :-) Tenhle seriálek jsem pojal jako takový rychloúvod do psaní pythonských modulů. Před měsícem jsem totiž dostal do situace, kdy mi podobný článek "pro blbé" docela dost chyběl a zbytečně strávil dlouhé hodiny hledáním informací v originální dokumentaci.
API umožňuje změnit i např. hodnotu jedinčky ;-)
Tedy, nezkoušel jsem to v praxi, takže to snad selže... ale když vytvoříte Int s hodnotou jedna, dostanete referenci na existující objekt (objekty malých čísel -- tuším 0 až 100 -- se neruší, existují pořád), jen s větším refcountem, jeho hodnotu můžete změnit např. některou PyNumber_InPlace* funkcí.
Někdo by to mohl zkusit...
Že chyba v modulu shodí interpret, to je celkem jasné a těžko s tím v případě C něco udělat.
Ale tady jde o to, že API přímo umožňuje dělat něco, co porušuje základní kontrakt, se kterým operuje programátor v daném jazyce, a nijak se mi to nesnaží znemožnit. To se mi zdá být docela v rozporu s jistým tlakem na chování programátora v Pythonu (který považuji v zásadě za rozumný).
Osobně se domnívám, že funkce PyArg_ParseTuple (a případně jí podobné) by mohla přinejmenším vracet defenzivní kopie základních immutable typů, aby se zamezilo alespoň chybám z neznalosti či nedbalosti (ochránit to obecně před zlým úmyslem asi v C na sto procent nepůjde).
Podpora psani modulu v C je tam take proto, abychom mohli psat _rychle_ rutiny. To je snad jedina vec, ve ktere je Cecko dobre. PyArg_ParseTuple muze vracet kopie, ale my pak stejne muzeme nekde jinde dereferencovat neinicializovany ukazatel. Pokud bychom chteli vsechny jistoty, co nam dava Python, museli bychom to vsechno psat v Pythonu a vzdat se rychlosti.
Možná si tak docela nerozumíme, mně rozhodně nejde o pohodlí programátora modulů. Představme si ale například následující situaci:
===
import priklad
class MyClass:
name = ""
def setName(self, name):
self.name = name
def printName(self):
print self.name
s = "My Name"
m = MyClass()
m.setName(s)
m.printName()
priklad.otocit(s)
m.printName()
===
(odsazování kódu v příspěvku nepřežije, ale myslím, že to i tak bude snad srozumitelné)
Třída MyClass si při nastavení atributu name nevytvořila kopii parametru, protože programátor se téměř jako jednu z prvních informací o Pythonu dozvěděl, že řetězce jsou neměnné. Bohužel dokumentaci naopak příliš nečetl programátor modulu priklad a "protože to jde" upraví přímo obsah řetězce. A teď si představme, že třída MyClass používá atribut name například jako klíč pro načtení nějakých podstatných dat z databáze nebo jako jméno souboru či něco podobného. Kdybych jakožto programátor neočekával, že řetězce jsou neměnné, vytvořím si zcela jednoznačně vlastní kopii parametru. Takto se můžu ošklivě divit, co že se mi to vlastně z té databáze načítá.
Pokud by API vytvářelo kopie, ani líný programátor modulů, který nečte dokumentaci, by nic nemohl zkazit, protože by to jaksi samo od sebe nešlo. Pokud by chtěl škodit, zřejmě si nějak poradí, o tom není sporu.
Pokud by API vytvářelo kopie, bylo by nepoužitelné pro pomalost. (Řetězec není jen "Ahoj světe", ale třeba taky obrovský text z databáze, z něhož chcete použít jen pár znaků.) C API Pythonu není Python, je to z hlediska tohoto jazyka velmi nízkoúrovňová záležitost. Je celkem přirozené očekávat od nízkoúrovňového programátora základní znalosti a určitou obezřetnost. Psát modul v C je z pohledu Pythonisty totéž jako psát ovladač hardware z pohledu aplikačního programátora. V obou případech ztrácíte základní jistoty a můžete nadělat pořádnou paseku. A v obou případech musí být kód dostatečně rychlý.