class ITest( Interface ):
def test(self, t): pass
Mivel az Interface osztaly az InterfaceType konstruktorat orokolte (metaosztalyoknak is van konstruktoruk ami akkor fut le ha peldanyt hozunk letre belole, azaz normal osztalyt a metaosztalybol), az ITest pedig az ovet. Igy az ITest osztaly megkrealasakor, le fog futni a metaosztalyomban levo ellenorzo kod. Hangsulyozom hogy osztaly letrehozasakor, nem osztaly peldanyositasakor. Ezutan ezt az interfacet "implementalom", amihez ugyanugy az oroklest hasznalom.
class Test( ITest ):
def test(self, t):
return t
A Test osztaly letrehozasakor szinten lefut az ellenorzes. Mi is van ebben az ellenorzesbe. Eloszor is lekerdezi az aktualis osztaly metodusait. Jelen esetben a Test krealasakor ez a 'test' metodus. Lekerdezi az osztaly direkt oseit. Ha ennek az osnek az ose Interface osztaly akkor o maga egy Interface. ITest jelen esetben. Igy el tudom donteni hogy a Test osei kozul melyik az interface mert nem biztos hogy csak ITestbol fog szarmazni, lehet ott mas nem interface osztaly is. Tehat megvannak az implementalando interfacek (ITest vagy Interface osztaly letrehozasakor nem fog ilyet talalni ekkor nem is ellenoriz semmit). Az interfacek metodusait egyenkent lekerdezem, jelen esetben megkapom az Itest.test(self, t)-t, es megnezem hogy ez egyenlo-e (marmint 2 fuggveny referencia) az aktualis osztaly a Test, ugyanilyen nevu (mert hat biztos hogy van neki ilyen hiszen orokolte, kerdes hogy a user feluldefinialta-e) metodusaval. Ha a 2 referencia megegyezik akkor csak sima oroklesrol van szo, ha kulonbozo akkor felul lett definialva, azaz a user kozvetlenul beleirta a kodba, tehat implementalta. Ezutan leellenorozm meg a fuggveny parametereinek a szamat is, ha nem stimemel ugyanugy assert errorozik (azert assert, mert az letilthato egy parancsosoros parameterrel ha pl release verzional nem akarjuk ellenorizgetni). Szoval ha en azt mondom hogy class Test( ITest, Base1, Base2 ), es az ITest interfacebol szarmazik, a Base1/2 pedig normal osztaly, akkor az ITestben levo metodusokat koteles vagyok feluldefinialni.
Es akkor jojjon a kod:
from types import MethodType
def getMethods( clz ):
return [i for i in dir(clz) if type(getattr(clz, i)) == MethodType]
class InterfaceType(type):
def __new__(cls, name, bases, dct):
return type.__new__(cls, name, bases, dct)
def __init__(cls, name, bases, dct):
super(InterfaceType, cls).__init__(name, bases, dct)
for b in bases:
if b.__base__.__name__ == 'Interface':
imet = getMethods( b )
for m in imet:
assert getattr( cls, m ) != getattr( b, m ), 'WARNING: method %s not implemented in %s' % ( m, cls.__name__ )
assert getattr( cls, m ).func_code.co_argcount == getattr( b, m ).func_code.co_argcount, 'WARNING: method %s not correctly implemented in %s (parameter mismatch)' % ( m, cls.__name__ )
Interface = InterfaceType('Interface', (), {})
Mivel metaclassrol van szo lathato hogy konstruktorban nem self, hanem cls, azaz metaosztaly peldanyra mutato referencia van. A __new__ az __init__ elott hivodik meg es az uj osztaly peldanyaval ( ami most szinten osztaly mivel metaclassbol hozza letre ) ter vissza ('type' osztaly factorykent mukodik). Elofordulhat hogy ugy jon letre az osztaly hogy az __init__ nem hivodik meg pl picklevel serializaljuk be, ilyenkor az ellenorzes sem fog lefutni (normal osztaly betolteskor szerintem le kell mindig). De a 2 cls parameter nem ugyanaz a 2 fuggvenyben. A __new__ban a self-el analog a metaosztaly sajat referencia van, mig a konstruktorban az aktualis metaosztaly peldany, azaz normal osztalyra mutato referencia. A for loopban a direkt ososztalyokbol kivalasztjuk azokat akiknek az ose az Interface, es ezutan kovtkezik a metodusok osszehasonlitasa. Az utolso sor nagyon fontos, Interface = InterfaceType('Interface', (), {}), itt hozok letre az InterfaceType metaosztalybol egy peldanyt, azaz egy normal Interface osztalyt.
Itt pedig a test kod
from interface import Interface
class ITest( Interface ):
def test(self, t): pass
class Cloneable( Interface ):
def clone(self): pass
class Test( ITest, Cloneable ):
def lofasz(self):
return t
def test(self, t):
return t
def clone(self):
print 'clone'
class Test2( Test):
pass
A modszer meg erosen kiserleti, tesztelest igenyel, de elsore nem latok olyan problemakat mint az elozo kettonel.

0 comments:
Post a Comment