From: Richard T. <rjt...@th...> - 2009-07-17 13:56:05
|
There is no implicit checking that an interface is implemented. It is possible to 'explicitly' check that a class implements all of the methods specified in an interface but such a check is of limited value in Python. For instance, if you are using the __getattr__ delegation pattern there is no way to check that a class implements a particular set of methods unless you actually try to call them So the interfaces are meant as a declaration that an *object* (note: not a class) will provide an interface, at runtime. It is even possible for a given object to change the interfaces that it will support. In our case it would be possible to change the interfaces that the database object declared. So the IWriteDB interface could be removed if the database was flagged as readonly. Calling code can ask for an object to be converted to support a particular interface and if a method is available to do the conversion to will be called automatically. This approach is extreemly flexible but it can be difficult for newcomers to the code base to understand. It might not be a good thing for gramps to end up as difficult to work on as Zope :-) Regards Richard Benny Malengier wrote: > 2009/7/17 Gerald Britton <ger...@gm...>: >> At first glance, I don't like it much. It seems like trying to force >> Java idioms on Python. I really prefer a purely Pythonic approach. >> Plus, I try to avoid function calls whenever I can because they are >> still expensive in Python compared to other languages. The example >> where getattr does a lookup by calling names() is just the kind of >> thing I would try to avoid. > > But it is a nice structure, and probably it avoids the class > inheritence of an abstract class which would save some time > It looks like the gtk classes I mentioned that also use some sort of > implements structure. > > But you are right, the __getattr__ adds some extra calls. Note that > the slowdown is only present in one of the two cases: > > 1. the method is implemented ==> __getattr__ is never called, the > method is found, and executed > 2. the method is not implemented ==> __getattr__ is called, name > looked up in a dictionary, if present, db method is called. This as > opposed to calling the method that then does a call of db method > immediately. > > I think I would avoid the __getattr__ calling too, but the implements > idea remains valid in that case nevertheless as it removes the > abstract class inheritance, while still checking an interface is > actually implemented (I hope, did not look at the details of zope). > > Benny >> On Fri, Jul 17, 2009 at 7:36 AM, Richard >> Taylor<rjt...@th...> wrote: >>> Gerald >>> >>> This might be a step too far, but you could consider using >>> zope.interfaces (http://pypi.python.org/pypi/zope.interface/3.5.2). >>> (Don't worry about the "zope" name, it came from zope originally but is >>> now used by many projects outside of zope). >>> >>> zope.interface provide a framework for defining interfaces orthogonally >>> from the classes that implement them ("provide them" in zope.interface >>> speak). >>> >>> >>> Interfaces are defined just like normal classes but they inherit from >>> Interface: >>> >>> from zope.interface import Interface >>> >>> class IReadOnlyDB(Interface): >>> >>> def read(self, wibble): >>> """Docstring""" >>> pass #this can not provide an implementation >>> >>> A class can they state that it implements the Interface (and hence >>> objects of that class "provide" the interface). >>> >>> from zope.interface import implements >>> >>> class ReadOnlyDB: >>> implements(IReadOnlyDB,<plus other interfaces>) >>> >>> def read(self, wibble): >>> implementation >>> >>> >>> So a proxy class (using delegation) might look something like: >>> >>> >>> class ReadOnlyProxy: >>> implements(IReadOnlyDB) >>> >>> def __init__(self, c): >>> self.c = c >>> >>> def __getattr__(self,name): >>> if name in IReadOnlyDB.names(): >>> return getattr(self.c, name) >>> >>> db = ReadOnlyDB() >>> proxy = ReadOnlyProxy(db) >>> >>> Both db and proxy provide the same IReadOnlyDB interface. >>> >>> Note the line "IReadOnlyDB.names()" which demonstrates that you can >>> introspect the interface. >>> >>> This model is very powerful and enables lots of control of which objects >>> provide which interface without needing to use Abstract Base Classes and >>> ending up with complex multiple inheritance structures. Objects can be >>> 'verified' to check if they implement a particular interface, interfaces >>> can be inherited etc. >>> >>> Just a thought. >>> >>> Richard >>> >>> >>> >>> Gerald Britton wrote: >>>> On second thought I'm not sure that this is the way to go, since then >>>> we lose the abstract base class idea. If __getattr__ gobbles up all >>>> undefined methods, there's nowhere to put the docstring for the >>>> interface. In spite of the seeming waste of space, spelling it all >>>> out might just be the way to go. >>>> >>>> On Wed, Jul 15, 2009 at 12:50 PM, Gerald >>>> Britton<ger...@gm...> wrote: >>>>> The trouble is that the NotImplementedError is raised in __getattr__ >>>>> and you don't see the method you were trying to call. Of course the >>>>> traceback shows you where you came from so you can figure it out. >>>>> >>>>> The other thing I'm worried about is performance, though it seems that >>>>> a custom __getattr__ method does not add significant overhead (< 1%) >>>>> >>>>> I think I like this idea and will try to exploit it unless others object. >>>>> >>>>> On Wed, Jul 15, 2009 at 11:48 AM, Richard >>>>> Taylor<rjt...@th...> wrote: >>>>>> Hi >>>>>> >>>>>> Firstly, I have been out of Gramps development for some time so forgive >>>>>> me if my response is a little naive. >>>>>> >>>>>> I have been following this thread with interest. >>>>>> >>>>>> Taking a quick look at the current implementation of ProxyDbBase I am >>>>>> struck by the number of methods that are of the form: >>>>>> >>>>>> def method(self, arg): >>>>>> self.db.method(arg) >>>>>> >>>>>> This looks like a classic 'delegate' pattern. It would be better to deal >>>>>> with this using a __getattr__ delegation approach e.g.: >>>>>> >>>>>> >>>>>> class C: >>>>>> def method(self,a): >>>>>> print "C ",a >>>>>> >>>>>> class D: >>>>>> def __init__(self,c): >>>>>> self.c = c >>>>>> >>>>>> def method2(self,a): >>>>>> print "D ",a >>>>>> >>>>>> def __getattr__(self,name): >>>>>> return getattr(self.c, name) >>>>>> >>>>>> The methods that should not be delegated could then be put into a list >>>>>> that is checked in __getattr__: >>>>>> >>>>>> class D: >>>>>> __do_not_delegate__ = ('methodA','methodB') >>>>>> >>>>>> def __getattr__(self,name): >>>>>> if name in __do_not_delegate__: >>>>>> raise NotImplemented >>>>>> >>>>>> return getattr(self.c, name) >>>>>> >>>>>> This at least would cut down on the boiler plate in the classes. >>>>>> >>>>>> Just a thought. >>>>>> >>>>>> Richard >>>>>> >>>>>> >>>>>> Gerald Britton wrote: >>>>>>> I've been pondering the right way to go about restructuring the >>>>>>> database objects while minimizing disruption. Here's a possible plan >>>>>>> for discussion: >>>>>>> >>>>>>> End result: >>>>>>> >>>>>>> GrampsDbBase object, containing only abstract methods all returning >>>>>>> NotImplementedError >>>>>>> GrampsDbRead object, derives from GrampsDbBase and implementing only >>>>>>> the methods needed to read the database >>>>>>> GrampsDbWrite object, derives from GrampsDbRead and implementing the >>>>>>> methods need to write the database (thus comprising a full set of >>>>>>> methods, those inherited from GrampsDbRead and those implemented in >>>>>>> GrampsDbWrite) >>>>>>> >>>>>>> Here are two options for the proxy databases: >>>>>>> >>>>>>> 1. ProxyDbRead object, subclassed from GrampsDbRead and overriding its >>>>>>> methods to add conditional logic (see below) >>>>>>> >>>>>>> OR >>>>>>> >>>>>>> 2. ProxyDbBase object, subclassed from GrampsDbBase, possibly adding >>>>>>> some abstract methods (not sure there are or should be any to add, >>>>>>> though) >>>>>>> ProxyDbRead, subclassed from ProxyDbBase, implementing basic methods >>>>>>> to read a proxy database with some conditional logic >>>>>>> >>>>>>> I'm partial to option 1 since it builds on earlier work but that >>>>>>> depends on how much of GrampsDbRead is directly useful in ProxyDbRead. >>>>>>> >>>>>>> Whichever option is chosen (or some other scheme, if neither of these >>>>>>> is appropriate), the actual proxy database objects (currently four: >>>>>>> PrivateProxyDb, LivingProxyDb, FilterProxyDb, and ReferencedProxyDb) >>>>>>> would derive from ProxyDbRead. >>>>>>> >>>>>>> My thoughts for ProxyDbRead include implementing a predicate scheme >>>>>>> for use as a kind of callback to indicate if an object should be >>>>>>> included or not. I did this already in the iter_... functions >>>>>>> recently committed, which by default now return: >>>>>>> >>>>>>> ifilter(include_something, self.db.iter_something_handles()) >>>>>>> >>>>>>> where "include_something" is a predicate that is passed the handle >>>>>>> being considered. If not overridden in the subclass, it defaults to >>>>>>> None which tells ifilter to return any handle from the iterator if it >>>>>>> evaluates to boolean True. A good example is for the PrivateProxyDb, >>>>>>> where I can define: >>>>>>> >>>>>>> def include_person(self, handle): >>>>>>> person = self.db.get_person_from_handle() >>>>>>> if person: >>>>>>> return not person.__isprivate() >>>>>>> return False >>>>>>> >>>>>>> This forces the iterator to only return person objects NOT marked >>>>>>> private. The same idea can be used in other default methods as well, >>>>>>> such as: >>>>>>> >>>>>>> get_something_from_handle: Only return an object if the predicate >>>>>>> evaluates true >>>>>>> get_something_from_id: Same as above >>>>>>> has_something_handle: Only return True if the predicate is >>>>>>> True for this object, otherwise return False >>>>>>> >>>>>>> etc. >>>>>>> >>>>>>> This way, the child proxy database classes have much less to >>>>>>> implement. In some cases, the predicate functions will be sufficient. >>>>>>> >>>>>>> Please consider these ideas and provide feedback. I want to do the >>>>>>> right thing, here. >>>>>>> >>>>>>> >>>>> >>>>> -- >>>>> Gerald Britton >>>>> >>>> >>>> >>> >> >> >> -- >> Gerald Britton >> >> ------------------------------------------------------------------------------ >> Enter the BlackBerry Developer Challenge >> This is your chance to win up to $100,000 in prizes! For a limited time, >> vendors submitting new applications to BlackBerry App World(TM) will have >> the opportunity to enter the BlackBerry Developer Challenge. See full prize >> details at: http://p.sf.net/sfu/Challenge >> _______________________________________________ >> Gramps-devel mailing list >> Gra...@li... >> https://lists.sourceforge.net/lists/listinfo/gramps-devel >> > > > |