FAQ
I have a fairly simple bit of code, something like:

# This should be importing the subclasses somehow, so that the factory
# can make them.
# import Parser.One
# import Parser.Two
# or.. from Parser import *?
class Parser():
def parse:
'Implemented only in subclass'

def make_parser(which_parser):
if(which_parser = 'one'):
return One()
else:
return Two()

# import Parser?
class One(Parser):
def parse:
'one implementation'

class Two(Parser):
def parse:
'another implementation'

The problem I have is that I don't understand how to put this into
actual files in actual directories and have the interpreter do
something actually useful :-) . What I would like to do is something
like:

lib/
Parser.py
Parser/
__init__.py (maybe?)
One.py
Two.py

But I'm not clear on how to structure the import statements. I'm a bit
of a newb wrt python, and I get any number of different errors depending
on how I arrange the import statements, everything from

AttributeError: 'module' object has no attribute 'make_parser'
to
ImportError: cannot import name
to
TypeError: Error when calling the metaclass bases

depending on how I use import. Nothing seems to be the correct
combination. Any help would be much appreciated!

Austin

Search Discussions

  • Benjamin Kaplan at Mar 20, 2009 at 12:52 am

    On Thu, Mar 19, 2009 at 6:52 PM, Austin Schutz wrote:
    I have a fairly simple bit of code, something like:

    # This should be importing the subclasses somehow, so that the factory
    # can make them.
    # import Parser.One
    # import Parser.Two
    # or.. from Parser import *?
    class Parser():
    def parse:
    'Implemented only in subclass'

    def make_parser(which_parser):
    if(which_parser = 'one'):
    return One()
    else:
    return Two()

    # import Parser?
    class One(Parser):
    def parse:
    'one implementation'

    class Two(Parser):
    def parse:
    'another implementation'

    The problem I have is that I don't understand how to put this into
    actual files in actual directories and have the interpreter do
    something actually useful :-) . What I would like to do is something
    like:

    lib/
    Parser.py
    Parser/
    __init__.py (maybe?)
    One.py
    Two.py

    But I'm not clear on how to structure the import statements. I'm a bit
    of a newb wrt python, and I get any number of different errors depending
    on how I arrange the import statements, everything from

    AttributeError: 'module' object has no attribute 'make_parser'
    to
    ImportError: cannot import name
    to
    TypeError: Error when calling the metaclass bases

    depending on how I use import. Nothing seems to be the correct
    combination. Any help would be much appreciated!

    Austin
    This seems to be a good use for a metaclass, though it's tricky to work
    around the fact that the new class doesn't exist when the metaclass is
    called.To get around this, I have the metaclass add the new Parser to a
    "to-do" list. The metaclass will be called whenever a subclass is created,
    so every class that subclasses parser will automatically be added.

    BTW, don't create a class and a package with the same name. When you import
    Parser, python won't know whether you want Parser.py or Parser/__init__.py.

    I'm not very good with metaclasses, so there might be a couple mistakes in
    here. Note that this won't work if you run this as the main script because
    then you end up with two copies of Parser
    (Parser.Parser and __main__.Parser).



    class ParserMeta(type) : #the metaclass is called when the class is created
    def __new__(cls, *args) :
    #args = (name, bases, dict). dict has __module__ in it.
    #we don't want to add the base class
    if args[0] != "Parser" :
    Parser._not_loaded_parsers.append(args)
    return type.__new__(cls, *args)


    #note that this is for Python 3.
    #for python 2.x do:
    #class Parser(object) :
    # __metaclass__ = ParserMeta
    class Parser(metaclass=ParserMeta) :
    _not_loaded_parsers = [] #the parsers that aren't loaded yet
    parsers = {} #the list of parsers- names mapped to the class.
    def parse(self) :
    raise NotImplementedError

    @classmethod
    def load(cls) :
    """ This method will add the parsers to the dictionary if they aren't
    already in there"""
    while cls._not_loaded_parsers :
    new_parser = cls._not_loaded_parsers.pop()
    mod = __import__(new_parser[2]['__module__'], fromlist=['*'])
    cls.parsers[new_parser[0]] = mod.__dict__[new_parser[0]]

    @classmethod
    def make_parser(cls, which_one) :
    """loads the parsers if necessary and returns the parsers"""
    cls.load()
    return cls.parsers[which_one]()


    import modules-that-contain-other-parsers

    Parser.load()
  • R. David Murray at Mar 20, 2009 at 1:11 am

    Austin Schutz wrote:
    I have a fairly simple bit of code, something like:

    # This should be importing the subclasses somehow, so that the factory
    # can make them.
    # import Parser.One
    # import Parser.Two
    # or.. from Parser import *?
    class Parser():
    def parse:
    'Implemented only in subclass'
    Hmm. You need to go back to the tutorial, I think :)

    This should be

    def parse(self):
    raise NotImplementedError

    The raise NotImplementedError is the pythonic way of indicating
    unimplemented methods in a superclass. But also take a look
    at ABCs (Abstract Base Classes), which provide some nice sugar
    for this kind of thing.
    def make_parser(which_parser):
    if(which_parser = 'one'):
    return One()
    else:
    return Two()
    Skip this, I'll rewrite it later.
    # import Parser?
    class One(Parser):
    def parse:
    'one implementation'
    Again, 'def parse(self):'. Might want to make the body something
    like "print 'one implementation'" so you can tell if you got
    the correct parser when you test it.
    class Two(Parser):
    def parse:
    'another implementation'
    Same comments as above.
    The problem I have is that I don't understand how to put this into
    actual files in actual directories and have the interpreter do
    something actually useful :-) . What I would like to do is something
    like:

    lib/
    Parser.py
    Parser/
    Well, there's your first import mistake :). You can't have a module
    and a package both with the same name, nor do you need to. So drop the
    'Parser.py'. And rename the directory 'parser'; module and package
    names are lower case according to the Python style guide.
    __init__.py (maybe?)
    Yes. Required if you are going to have a package you can import
    things from.
    One.py
    Two.py
    Again, rename these to 'one.py' and 'two.py'. Then in your
    __init__.py file, do:

    from one import One
    from two import Two

    Now, in your code that uses the parser, do:

    from parser import One as Parser

    or

    from parser import Two as Parser

    depending on which parser you want.

    Unless your parsers are really heavy, you could probably just
    keep all this stuff in one file, parser.py...but if they are complex
    enough you want to split the source up, and you want a base class
    or ABC, then put that in another file (parserbase.py, perhaps),
    and at the top of each of the individual parser files do

    from parserbase import ParserBase

    (or some such name).

    Now, if you really need the parser to instantiate to be chosen at run
    time via a string, you could add something like this to your __init__.py:

    def parserFactory(which_parser):
    return globals()[which_parser.capitalize()]()

    which will look up the capitalized version of the string (eg: 'One')
    in the __init__.py module's global namespace, thus picking up the
    class, and then calls it to create an instance, which is then
    returned.

    Then your code that uses this can do:

    from parser import parserFactory

    myparser = parserFactory('one')

    From your class heavy patterns I am guessing you are coming from
    Java or some similar language....you don't have to work as hard
    to get things done in Python.

    --
    R. David Murray http://www.bitdance.com

Related Discussions

Discussion Navigation
viewthread | post
Discussion Overview
grouppython-list @
categoriespython
postedMar 19, '09 at 10:52p
activeMar 20, '09 at 1:11a
posts3
users3
websitepython.org

People

Translate

site design / logo © 2022 Grokbase