How to tell which subclass was used to instantiate object

Discussion in 'Python' started by Frank Millman, May 1, 2004.

  1. Hi all

    I have a question regarding inheritance. I have come up with a
    solution, but it is not very elegant - I am sure there is a more
    pythonic approach. Assume the following class definitions.

    class Table:
    def __init__(self,table_name,table_type):

    class Master(Table):
    def __init__(self,table_name,table_type):
    Table.__init__(self,table_name,table_type)

    class Transaction(Table):
    def __init__(self,table_name,table_type):
    Table.__init__(self,table_name,table_type)

    class Armaster(Master):
    def __init__(self,table_name,table_type):
    Master.__init__(self,table_name,table_type)

    Table is the main class, which represents a database table. Master is
    a subclass that encapsulates various common characteristics of a
    'master file' type of table. Transaction is a subclass that
    encapsulates various common characteristics of a 'transaction file'
    type of table. ArMaster is a subclass that is created to handle
    specific requirements for the ArMaster table, which is a 'master file'
    type of table.

    Both of the following would be errors -

    class Armaster(Table):
    def __init__(self,table_name,table_type):
    Table.__init__(self,table_name,table_type)

    class Armaster(Transaction):
    def __init__(self,table_name,table_type):
    Transaction.__init__(self,table_name,table_type)

    By inheriting from the incorrect class, the special methods to handle
    a 'Master' type table have been bypassed. My question is, how can
    Table check that objects have inherited from the correct subclasses?

    Here is my inelegant solution. Assume that table_type contains the
    string 'Master'.

    class Master(Table):
    def __init__(self,table_name,table_type):
    self.my_type = 'Master'
    Table.__init__(self,table_name,table_type)

    class Table:
    def __init__(self,table_name,table_type):
    if hasattr(self,'my_type'):
    ok = (self.my_type == table_type)
    else:
    ok = False
    if not ok:
    raise RuntimeError('%s must be of type %s' %
    (table_name,table_type))

    Is there a more direct way for a top-level class to determine which
    subclasses were used to instantiate it?

    Thanks

    Frank Millman
    Frank Millman, May 1, 2004
    #1
    1. Advertising

  2. Frank Millman

    John Roth Guest

    "Frank Millman" <> wrote in message
    news:...
    > Hi all
    >
    > I have a question regarding inheritance. I have come up with a
    > solution, but it is not very elegant - I am sure there is a more
    > pythonic approach. Assume the following class definitions.
    >

    [...]
    >
    > Is there a more direct way for a top-level class to determine which
    > subclasses were used to instantiate it?


    Look at the __class__ attribute. Specifically, __class__.__name__
    should tell you the name of the class.

    John Roth
    >
    > Thanks
    >
    > Frank Millman
    John Roth, May 1, 2004
    #2
    1. Advertising

  3. On 1 May 2004 05:47:20 -0700,
    (Frank Millman) wrote:

    [ details of a multi-level object hierarchy, and worries of __init__
    methods bypassing it ]

    > By inheriting from the incorrect class, the special methods to
    > handle a 'Master' type table have been bypassed. My question is,
    > how can Table check that objects have inherited from the correct
    > subclasses?


    Python usually takes the "we're all adults here" point of view,
    and leaves that question to unit tests and/or code reviews. No
    amount of B&D is sufficient to protect from a malicious coder
    anyway.

    > Here is my inelegant solution. Assume that table_type contains
    > the string 'Master'.


    > class Master(Table):
    > def __init__(self,table_name,table_type):
    > self.my_type = 'Master'
    > Table.__init__(self,table_name,table_type)


    > class Table:
    > def __init__(self,table_name,table_type):
    > if hasattr(self,'my_type'):
    > ok = (self.my_type == table_type)
    > else:
    > ok = False
    > if not ok:
    > raise RuntimeError('%s must be of type %s' %
    > (table_name,table_type))


    > Is there a more direct way for a top-level class to determine
    > which subclasses were used to instantiate it?


    You could add another (optional) parameter to Table.__init__, but
    that's really the same solution with new syntactic sugar.

    You could unwind the stack frame and look at who's calling
    Table.__init__, but that's rather un-Pythonic, too.

    IMO, the most Pythonic solution is to provide factory functions
    that do the Right Thing instead of instantiating your classes
    directly from your application code.

    HTH,
    Heather

    --
    Heather Coppersmith
    That's not right; that's not even wrong. -- Wolfgang Pauli
    Heather Coppersmith, May 1, 2004
    #3
  4. "John Roth" <> wrote in message news:<>...
    > "Frank Millman" <> wrote in message
    > news:...
    > > Hi all
    > >
    > > I have a question regarding inheritance. I have come up with a
    > > solution, but it is not very elegant - I am sure there is a more
    > > pythonic approach. Assume the following class definitions.
    > >

    > [...]
    > >
    > > Is there a more direct way for a top-level class to determine which
    > > subclasses were used to instantiate it?

    >
    > Look at the __class__ attribute. Specifically, __class__.__name__
    > should tell you the name of the class.
    >
    > John Roth
    > >


    Thanks for the reply, John. Unfortunately this does not seem to give
    me what I am looking for. I can only look at the __class__ attribute
    once the object has been instantiated.

    I want to check the inheritance hierarchy from the top-level __init__
    while the object is being instantiated, and raise an exception if it
    fails my test.

    It seems from Heather's reply that it is not possible to do this
    directly, and therefore my inelegant solution is probably as good as
    any. See my reply to Heather for further comments.

    Thanks again.

    Frank
    Frank Millman, May 2, 2004
    #4
  5. Heather Coppersmith <> wrote in message news:<>...
    > On 1 May 2004 05:47:20 -0700,
    > (Frank Millman) wrote:
    >
    > [ details of a multi-level object hierarchy, and worries of __init__
    > methods bypassing it ]
    >
    > > By inheriting from the incorrect class, the special methods to
    > > handle a 'Master' type table have been bypassed. My question is,
    > > how can Table check that objects have inherited from the correct
    > > subclasses?

    >
    > Python usually takes the "we're all adults here" point of view,
    > and leaves that question to unit tests and/or code reviews. No
    > amount of B&D is sufficient to protect from a malicious coder
    > anyway.
    >


    I fully agree, but this is not the scenario I am trying to cater for.
    I will explain what I am doing - maybe you can suggest a better
    approach.

    I am writing a general-purpose accounting/business package. It is the
    nature of such beasts that no matter how powerful/generalised it is,
    certain users will always have special requirements. I am therefore
    including a large number of 'hooks' where users or their consultants
    can add their own code without interfering with the main body of code.
    One example of this is adding features to a particular database table
    definition.

    I have a standard class definition to represent a table, with various
    attributes and methods. I allow the user to create their own subclass
    for a particular table, adding/overriding where necessary. I have a
    function to 'open' a table. This uses a try/except to 'try' to import
    and instantiate a subclass for the table, 'except' instantiate the
    standard class.

    I have recently introduced the concept of a 'table type', such as
    'Master' or 'Transaction', and have written my own subclasses with
    standard methods to handle each type of table. The table type is
    passed as an argument to the 'open' function. which now checks for a
    user-defined subclass first, if not found checks the type to see if a
    standard subclass exists, if not found instantiates the main class.
    This works well.

    The concern is that a table may be of type Master, but a user may
    create their own subclass and inherit from Table instead of Master by
    mistake. I want to detect this error and raise an exception.

    >
    > You could unwind the stack frame and look at who's calling
    > Table.__init__, but that's rather un-Pythonic, too.
    >

    I do not know what this means, and I really do not want to know :)

    > IMO, the most Pythonic solution is to provide factory functions
    > that do the Right Thing instead of instantiating your classes
    > directly from your application code.
    >


    If you can give a simple example, I would be interested in learning
    more about this. Alternatively, I will leave things as they are for
    now, and focus on getting my app to a point where I can get some live
    installations. If things look promising, I will release it as Open
    Source, and then I will be very happy to get feedback on this and, I
    am sure, many other aspects of my coding that could be substantially
    improved.

    > HTH,
    > Heather


    Many thanks for your input.

    Frank

    ps What does B&D mean?
    Frank Millman, May 2, 2004
    #5
  6. Frank Millman

    Peter Otten Guest

    Frank Millman wrote:

    > I have recently introduced the concept of a 'table type', such as
    > 'Master' or 'Transaction', and have written my own subclasses with
    > standard methods to handle each type of table. The table type is
    > passed as an argument to the 'open' function. which now checks for a
    > user-defined subclass first, if not found checks the type to see if a
    > standard subclass exists, if not found instantiates the main class.
    > This works well.
    >
    > The concern is that a table may be of type Master, but a user may
    > create their own subclass and inherit from Table instead of Master by
    > mistake. I want to detect this error and raise an exception.


    How about providing a subclass for every table the user might want to
    subclass:

    class Table:
    def __init__(self, name=None):
    if name is None:
    try:
    name = self.name
    except AttributeError:
    name = self.__class__.__name__
    self.name = name

    class Master(Table):
    pass

    class Transaction(Table):
    pass

    # provide a suggestively named class for every table in your application
    class Employees(Table): pass
    class Departments(Master): pass
    class Invoices(Transaction):
    name = "not-a-legal-identifier"


    for cls in [Employees, Departments, Invoices]:
    print cls().name


    Now the user can just subclass Employees without having to care whether it
    has to be derived from Transaction, Master, Table or whatever.
    If he needs to know, he can discover it on the command line:

    >>> issubclass(Employees, Transaction)

    False
    >>> issubclass(Invoices, Transaction)

    True
    >>>


    An additional benefit is that user code is shielded to some extent from
    modifications in your code, e. g., you could later change the base of
    Employees from Table to Master without requiring changes in client code.

    Peter
    Peter Otten, May 2, 2004
    #6
  7. Frank Millman

    Terry Reedy Guest

    "Frank Millman" <> wrote in message
    news:...
    > Heather Coppersmith <> wrote in message

    news:<>...
    > > Python usually takes the "we're all adults here" point of view,
    > > and leaves that question to unit tests and/or code reviews. No
    > > amount of B&D is sufficient to protect from a malicious coder
    > > anyway.


    > ps What does B&D mean?


    Bondage and Discipline
    Terry Reedy, May 2, 2004
    #7
  8. Frank Millman

    Yermat Guest

    Frank Millman wrote:
    > Hi all
    >
    > I have a question regarding inheritance. I have come up with a
    > solution, but it is not very elegant - I am sure there is a more
    > pythonic approach. Assume the following class definitions.
    >
    > class Table:
    > def __init__(self,table_name,table_type):
    >
    > class Master(Table):
    > def __init__(self,table_name,table_type):
    > Table.__init__(self,table_name,table_type)
    >
    > class Transaction(Table):
    > def __init__(self,table_name,table_type):
    > Table.__init__(self,table_name,table_type)
    >
    > class Armaster(Master):
    > def __init__(self,table_name,table_type):
    > Master.__init__(self,table_name,table_type)
    >
    >[...]
    >
    > By inheriting from the incorrect class, the special methods to handle
    > a 'Master' type table have been bypassed. My question is, how can
    > Table check that objects have inherited from the correct subclasses?
    >
    > Here is my inelegant solution. Assume that table_type contains the
    > string 'Master'.
    >
    > class Master(Table):
    > def __init__(self,table_name,table_type):
    > self.my_type = 'Master'
    > Table.__init__(self,table_name,table_type)
    >
    > class Table:
    > def __init__(self,table_name,table_type):
    > if hasattr(self,'my_type'):
    > ok = (self.my_type == table_type)
    > else:
    > ok = False
    > if not ok:
    > raise RuntimeError('%s must be of type %s' %
    > (table_name,table_type))
    >
    > Is there a more direct way for a top-level class to determine which
    > subclasses were used to instantiate it?


    If you are using new-style class, you should look at mro
    (http://www.python.org/2.3/mro.html). It will give you a linearization
    of the inheritance tree and thus check that object are inheriting good
    class...

    >>> class Table(object):

    .... def __init__(self, table_name, table_type):
    .... pass
    ....
    >>> class Master(Table):

    .... def __init__(self, table_name, table_type):
    .... super(Master, self).__init__(table_name, table_type)
    ....
    >>> class Transaction(Table):

    .... def __init__(self, table_name, table_type):
    .... super(Transaction, self).__init__(table_name, table_type)
    ....
    >>> class Armaster(Master):

    .... def __init__(self, table_name, table_type):
    .... super(Armaster, self).__init__(table_name, table_type)
    ....
    >>> Armaster.mro()

    [<class '__main__.Armaster'>, <class '__main__.Master'>, <class
    '__main__.Table'>, <type 'object'>]
    >>> Master.mro()

    [<class '__main__.Master'>, <class '__main__.Table'>, <type 'object'>]
    >>> Transaction.mro()

    [<class '__main__.Transaction'>, <class '__main__.Table'>, <type object'>]

    --
    Yermat
    Yermat, May 3, 2004
    #8
  9. (Frank Millman) wrote in message news:<>...
    > "John Roth" <> wrote in message news:<>...
    > > "Frank Millman" <> wrote in message
    > > news:...
    > > > Hi all
    > > >
    > > > I have a question regarding inheritance. I have come up with a
    > > > solution, but it is not very elegant - I am sure there is a more
    > > > pythonic approach. Assume the following class definitions.
    > > >

    > [...]
    > > >
    > > > Is there a more direct way for a top-level class to determine which
    > > > subclasses were used to instantiate it?

    > >
    > > Look at the __class__ attribute. Specifically, __class__.__name__
    > > should tell you the name of the class.
    > >
    > > John Roth
    > > >

    >
    > Thanks for the reply, John. Unfortunately this does not seem to give
    > me what I am looking for. I can only look at the __class__ attribute
    > once the object has been instantiated.
    >

    My apologies, John. You are quite correct, and I am talking rubbish.

    The following are all accessible either during init() or after
    instantiation -

    __class__ is the class object.

    __class__.__name__ is the name of the class.

    __class__.__bases__ is a tuple containing the base classes of the
    class. At first, I thought that the tuple would contain the
    inheritance hierarchy, but on experimenting I see that, if you use
    multiple inheritance, it contains all the classes used to define this
    class. As I am not using multiple inheritance, the tuple will contain
    1 or 0 elements. If you need the full hierarchy, you can walk through
    the __bases__ tuples until you reach an empty tuple.

    __class__.__bases__[0].__name__ is the name of the class that this
    class has been inherited from, which is exactly what I am looking for.

    Thanks for pointing me in the right direction.

    Frank
    Frank Millman, May 3, 2004
    #9
  10. Peter Otten <> wrote in message news:<c72ch0$6nk$04$-online.com>...
    > Frank Millman wrote:
    >
    > > I have recently introduced the concept of a 'table type', such as
    > > 'Master' or 'Transaction', and have written my own subclasses with
    > > standard methods to handle each type of table. The table type is
    > > passed as an argument to the 'open' function. which now checks for a
    > > user-defined subclass first, if not found checks the type to see if a
    > > standard subclass exists, if not found instantiates the main class.
    > > This works well.
    > >
    > > The concern is that a table may be of type Master, but a user may
    > > create their own subclass and inherit from Table instead of Master by
    > > mistake. I want to detect this error and raise an exception.

    >
    > How about providing a subclass for every table the user might want to
    > subclass:
    >
    > class Table:
    > def __init__(self, name=None):
    > if name is None:
    > try:
    > name = self.name
    > except AttributeError:
    > name = self.__class__.__name__
    > self.name = name
    >
    > class Master(Table):
    > pass
    >
    > class Transaction(Table):
    > pass
    >
    > # provide a suggestively named class for every table in your application
    > class Employees(Table): pass
    > class Departments(Master): pass
    > class Invoices(Transaction):
    > name = "not-a-legal-identifier"
    >
    >
    > for cls in [Employees, Departments, Invoices]:
    > print cls().name
    >
    >
    > Now the user can just subclass Employees without having to care whether it
    > has to be derived from Transaction, Master, Table or whatever.
    > If he needs to know, he can discover it on the command line:
    >
    > >>> issubclass(Employees, Transaction)

    > False
    > >>> issubclass(Invoices, Transaction)

    > True
    > >>>

    >
    > An additional benefit is that user code is shielded to some extent from
    > modifications in your code, e. g., you could later change the base of
    > Employees from Table to Master without requiring changes in client code.
    >
    > Peter


    Thanks a lot for this, Peter. I will give it some thought. However, as
    John has given me an easy answer, I will stick with that for now.

    Frank
    Frank Millman, May 3, 2004
    #10
  11. Frank Millman

    Roger Binns Guest

    Roger Binns, May 4, 2004
    #11
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. jstorta
    Replies:
    3
    Views:
    442
    jstorta
    Feb 20, 2006
  2. Ook
    Replies:
    2
    Views:
    357
  3. S.Volkov
    Replies:
    2
    Views:
    215
    S.Volkov
    Mar 12, 2006
  4. Trans
    Replies:
    8
    Views:
    320
    Robert Klemme
    Oct 23, 2008
  5. Fab

    Subclass of subclass

    Fab, Aug 9, 2012, in forum: C++
    Replies:
    0
    Views:
    396
Loading...

Share This Page