Archive

Archive for May, 2009

Dinamičko učitavanje modula u Pythonu

May 18th, 2009 Senko No comments

(Ovo je drugi u nizu postova o Python programiranju koji sam započeo postom o ubacivanju metoda u postojeće Python objekte)

Nedavno sam za jedan svoj projekt razvio sustav koji ima osnovni dio i nekolicinu plugina/modula/drivera za specifičnu funkcionalnost. Sustav sam organizirao tako da je svaki od plugina bio u posebnom Python modulu, svi su moduli bili u jednom direktoriju a __init__.py iz tog direktorija sadržavao je kod za importanje svakog od pojedinih modula. Nezadovoljan time što imam 20 importova koje moram prepravljati svaki put kad dodam ili maknem plugin, odlučio sam učitavanje modula malo automatizirati.

Kako automatizirati otkrivanje i učitavanje modula u Python programu? Jednostavan način je pročešljati traženi direktorij, vidjeti postoji li nešto što izgleda kao Python source file i pokušati prilikom izvođenja programa importati taj modul. Problematičan dio je ubacivanje imporanih podataka tamo gdje želimo, odnosno pojednostavniti njihovo dohvaćanje iz ostatka programa.

Prvo kod (koji ide u plugins/__init__.py), onda objašnjenje:

  plugins = []
  for entry in os.listdir(os.path.dirname(__file__)):
      if entry.endswith('.py') and entry != '__init__.py':
          entry = entry[:-3]
          try:
              m = __import__(__name__ + '.' + entry)
              m = getattr(m, entry)
              p = getattr(m, 'MyPluginClass')
              plugins.append(p)
          except Exception, e:
              print "Error while importing plugin %s: %s" % (entry, e)

Iz lokacije trenutnog modula saznaje se koji direktorij treba pregledavati za module - za svaku Python datoteku iz tog direktorija (osim samog sebe), probamo napraviti import modula (kako su svi pluginovi podmoduli trenutnog, trenutno ime modula se koristi kako bi se dobio puno ime modula za import). Nakon toga iz modul objekta izvučemo referencu na MyPluginClass (naziv klase u svakom od pluginova), iz koje kasnije možemo instancirati objekte iz pojedinih pluginova. Ukoliko nešto pođe po zlu, samo ignoriramo trenutnu datoteku i pokušavamo dalje.

Stvar koja ovdje izgleda čudno je dvostruki getattr. Naime, __import__ zapravo vraća referencu na trenutni modul, pa nakon toga prvo moramo dobiti referencu na modul koji učitavamo, te naposlijetku i na klasu unutar modula.

Uz kod još jedna napomena, dio koji odlučuje koju datoteku učitati na osnovu ekstenzije pretpostavlja da python datoteke završavaju sa ".py", stoga bi ovu skriptu trebalo prilagoditi ako se koristi na sustavu koji koristi drugačije ekstenzije.

Categories: Uncategorized Tags:

Twitter je down, al’ ja imam Sparrw backup :-)

May 13th, 2009 Senko No comments

Sparrw
Dok pišem ovaj post, Twitter je down. Ali, u mom sidebaru se i dalje vide moji tweetovi. Kako?

Jednostavno, koristim Sparrw, tweet backup & remix servis koji je od nedavno pušten u pogon. Sparrw hvata sve moje tweetove i sprema ih na sigurno, pa nemam problema sa njihovim prikazivanjem čak niti kad je Twitter down ili ako slučajno pogubi sve moje tekstove (što nije toliko nevjerojatna pojava).

Osim backupa tweetova, Sparrw mi omogućuje i pregledavanje i pretraživanje onog što sam napisao, kreiranje dnevnih, tjednih ili mjesečnih sažetaka, te mailiranje tweetova (za slučaj da me strah da i Sparrw ne bude down).

Sve ovo je potpuno automatizirano i vrlo se jednostavno aktivira, bez ikakve specijalne registracije na sam servis. Uskoro planiram dodati i još novih i korisnih mogućnosti (ako imate koju ideju ili želju, slobodno mi javite).

Stoga, ne časite niti časka nego posjetite Sparrw, podesite si backup i nemojte više živjeti u strahu da će Twitter popapati vaše umotvorine u 140 znakova ili manje ;-)

Categories: Croatian Tags:

Twitter početnica

May 13th, 2009 Senko 1 comment

twitter-ptica
Sada kada Twitter polagano postaje sve poznatiji i u našim krajevima, evo par natuknica novopečenim Twitterašima da se što bolje snađu na novom servisu:

Podesite si profil
Na Twitteru se ljudi obično predstavljaju pravim imenom i slikom (ali nije pravilo, ukoliko baš ne želite), bez obzira na samo korisničko ime koje su odabrali. Stavljanjem imena (ili naziva organizacije/projekta/tvrtke za koju ste otvorili račun), kratkog opisa i web adrese omogućujete drugim Twitterašima da vas bolje upoznaju. Postavljanjem avatara pomažete da vas drugi korisnici shvate kao živu osobu a ne kao apstraktni konstrukt.

Smisleno pratite druge korisnike
Praćenje drugih Twitteraša (followanje) je temeljna stvar kod Twittera pomoću koje određujete o čemu (ili kome) želite čitati. Ukoliko vam je netko zanimljiv, pratite ga, ukoliko nije, nemojte. Nemojte pratiti nekog samo zato što on(a) prati vas - za razliku od Facebooka gdje se “ako si ti moj prijatelj onda sam i ja tvoj prijatelj” podrazumijeva, praćenje kod Twittera ne znači da vam je netko prijatelj ili ne, i ne mora biti obostrano. Nemojte nasjedati spamerima koji prate tisuće ljudi u nadi da će ih oni pratiti nazad, jer od praćenja Twitteraša koji vas ne zanimaju ne dobijate ništa, samo si uništavate doživljaj i radite gužvu u listi statusa.

Ne koristite Twitter kao RSS feed vašeg bloga
Ukoliko imate svoj blog (ili neki drugi servis) i tweetate obavijesti svaki put kad imate novi post, to je u redu. Ali nemojte svoj Twitter korisnički račun svesti na to - aktivno ga koristite za interakciju i komunikaciju sa svojim followerima (svjetli primjer ovoga je Jutarnji.hr).

Twitter nije IRC
Nemojte ići niti na drugu krajnost i koristiti Twitter samo za chat, jer će Twitterašima koji nalete na vas vaše poruke (izvučene iz konteksta konverzacije) biti potpuno nezanimljive. Imajte u vidu da je Twitter način da cijelom svijetu nešto kažete, pa će vaša poruka imati najbolji efekt ako je korisna/zanimljiva najvećem broju ljudi. Ukoliko naiđete na zanimljiv Tweet koji želite proslijediti dalje, običaj je napisati “RT @od_kog_ste_čuli poruka” (RT znači retweet) ili “poruka (via @od_kog_ste_čuli)”.

Dodatak: relativno nova opcija kod Twittera je i ReTweet gumb uz svaku poruku koju će poruku automatski proslijediti u vaše ime (bez da je morate ručno kopirati i dodavati RT na početak).

Twitter je vrlo javan (osim ako se sakrijete)
Na Twitteru je moguće svoje poruke sakriti tako da ih vide samo ljudi kojima ste dopustili da vas prate. Neki korisnici koriste Twitter na taj način, ali većina ih piše u javnosti (vidi prethodnu točku). Ukoliko i vi pišete u javnosti, imajte u vidu da bilo tko može pročitati vaše poruke i da beskonačno ostaju zapamćene, stoga pazite što pišete, da vam se ne dogodi nešto ovakvo.

Mala zemlja za veliku Zrikku
Ukoliko vam se sviđa koncept Twittera ali preferirate hrvatske servise ili želite biti član manjeg communitya ljudi, svratite na Zrikku, hrvatski pandan Twitteru. Zrikka funkcionira na isti način, ali se ljudi većinom znaju i atmosfera je domaćija.

Gdje početi
Ukoliko tek počinjete s Twitterom i ne znate koga bi pratili, možda vam ova lista hrvatskih organizacija/firmi/blogova na Twitteru da koju ideju. (Napomena: staro par mjeseci a Twitter se brzo razvija, tako da je već prilično zastarjela, ali dobar početak). Također bacite oko i na listu blogera koju je sastavio @ivanbrezakbrkan.

Ugodno tweetanje!

Sviđa vam se post? Retweetajte ga i/ili komentirajte ovdje ili na Twitteru.

Categories: Croatian Tags:

Ubacivanje metoda u postojeće Python objekte

May 11th, 2009 Senko 7 comments

U objektno-orjentiranim jezicima korištenje API-ja se često izvodi tako da postoji bazna klasa koja implementira neku funkcionalnost ali sadrži i dummy metode koje u baznoj klasi ne rade ništa nego služe kao hookovi koje podklasa može koristiti da bi se integrirala sa radom bazne klase.

Tipičan primjer ovoga su callbackovi. Ukoliko podklasa želi instalirati callback za neki događaj (npr lijevi klik mišem nad labelom) može overloadati metodu koja se poziva na taj događaj (a koja u baznoj klasi ne radi ništa).
U PyQT-u (sintaksa ide po sjećanju) to izgleda otprilike ovako:

  class MyLabel(QLabel):
    def onKeyPress(self, event):
        ...do something...

Dva problema s ovim su: ukoliko želimo različitu funkcionalnost u keypress eventu za nekoliko objekata, moramo kreirati nekoliko različitih podklasa; a ukoliko netko drugi kreira objekt, ponekad ne možemo odrediti objekt koje klase treba instancirati.

Tipičan pattern za rješavanje potonjeg problema je Factory, odnosno klasa koja opisuje koju klasu želite instancirati. Ali, kako je Python dinamički jezik, imamo i alternativu - monkey-patch.

Monkeypatchiranje je tehnika mijenjanja ponašanja već postojećeg koda prilikom izvršavanja. Stvar je prilično moćna i lako se upucati u nogu, pa se obično ne preporuča ukoliko postoje bolji/elegantniji načini za rješavanje problema. Ali ovdje će nam izvrsno poslužiti da bi modificirali ponašanje već instanciranog objekta.

Mali snippet Python koda s kojim ćemo raditi:

  class Foo(object):
    def foo(self):
      print "foo called"
    def bar(self):
      print "bar called"

  x = Foo()
  x.bar() # ispisati će "bar called"

Ideja nam je overrideati metodu bar da ispiše nešto drugo:

  class Bar(Foo):
    def bar(self):
      print "different bar called"

  x = Bar()
  x.bar() # ispisati će "different bar called"

Zasad nema iznenađenja, ovo je standardni OOP. Ali što ako već imamo definiran ‘x’ kao instancu od Foo? Prva stvar koju možemo napraviti je promjeniti mu klasu:

   x = Foo()
   x.bar() # ispisuje "bar called"
   x.__class__ = Bar  # mijenjamo klasu objektu
   x.bar() # ispisuje "different bar called"

Ako vam ovo djeluje suludo, dobrodošli u dinamičke jezike ;-)

Još uvijek imamo problem sa činjenicom da moramo imati definiciju klase Bar. Ukoliko samo želimo promjeniti jednu metodu, ne da nam se svaki put pisati klasu. U tom slučaju možemo napraviti slijedeće:

def baz(self):
  print "yet another bar"

def add_method(obj, meth):
  obj.__class__ = type('newclassname', (Foo,), { 'bar': meth })

add_method(x, baz)
x.bar() # ispisuje "yet another bar"

Ovdje smo u tijeku izvođenja kreirali novu klasu “baz” koja naslijeđuje klasu Foo a ima jednu metodu zvanu “bar”, i promjenili postojećeg objekta u novkreiranu klasu.

Kao što se vidi, ovime možemo raditi prilično moćne modifikacije već postojećeg koda prilikom samog izvršavanja. Ali ovaj specifičan slučaj možemo rješiti i nešto jednostavnije i elegantnije:

def baz(self):
  print "yet another bar"

def add_method(obj, meth):
  setattr(obj, meth.__name__, lambda *args, **kwargs: meth(obj, *args, **kwargs))

add_method(x, baz)
x.baz() # ispisuje "yet another bar"

Ovaj put nismo morali generirati novu jednokratnu klasu samo da bi dodali metodu, nego smo funkciju dodali kao atribut objekta. Dodanu funkciju Python ne tretira magično kao metodu - da smo je samo dodali kao takvu, Python bi je tretirao kao obični callable atribut i samo je pozvao, pa kao prvi argument ne bi bila proslijeđena referenca na sam objekt. Kako bi se funkcija pozvala sa prvim argumentom objektom, morali smo kreirati closure koji je boundao referencu na objekt i proslijedio je funkciji kao prvi argument (tome služi ona lambda).

Update: Popravio poziv funkcije u zadnjem snippetu iz “x.bar()” u “x.baz()”, thx @nikolaplejic na uočavanju greške. Usput sam pokušao i malo pojasniti zašto je potrebno wrappati metodu i ručno proslijediti referencu na objekt.

Update 2: Ideja u članku je dodati metodu pojedinoj instanci objekta, ne proširiti cijelu klasu. Ukoliko želimo prilikom izvođenja dodati (ili izmjeniti) metode u nekoj klasi (a da to odmah bude vidljivo svim trenutnim i budućim instancama te klase, možemo napraviti slijedeće:

def baz(self):
  print "yet another bar"

x = Foo()
x.bar() # ispisuje "bar called"
Foo.bar = baz
x = Foo()
x.bar() # ispisuje "yet another bar"

U tom slučaju će se python čak pobrinuti oko automatskog bindanja metode instanci i proslijeđivanja reference kao prvog argumenta, i ovakav monkeypatching je relativno čest (iako još uvijek opasan ako se zloupotrebljava :) u Pyhtonu.

Categories: Croatian Tags: