finding abc's

Discussion in 'Python' started by lars van gemerden, Jan 25, 2013.

  1. Hi all,

    i was writing a function to determine the common base class of a number classes:

    def common_base(classes):
    if not len(classes):
    return None
    common = set(classes.pop().mro())
    for cls in classes:
    common.intersection_update(cls.mro())
    while len(common) > 1:
    cls1 = common.pop()
    cls2 = common.pop()
    if issubclass(cls1, cls2):
    common.add(cls1)
    elif issubclass(cls2, cls1):
    common.add(cls2)
    return common.pop()

    and ran common_base(int, float), hoping to get numbers.Number.

    this did not work because abstract base classes are not always in the mro() of classes.

    My question is: is there a way to obtain the abc's of a class or otherwise a way to make the function above take abc's into account (maybe via a predefined function)?

    Cheers, Lars
     
    lars van gemerden, Jan 25, 2013
    #1
    1. Advertising

  2. lars van gemerden

    Ian Kelly Guest

    On Fri, Jan 25, 2013 at 10:40 AM, lars van gemerden
    <> wrote:
    > Hi all,
    >
    > i was writing a function to determine the common base class of a number classes:
    >

    [...]
    >
    > and ran common_base(int, float), hoping to get numbers.Number.
    >
    > this did not work because abstract base classes are not always in the mro() of classes.
    >
    > My question is: is there a way to obtain the abc's of a class or otherwise a way to make the function above take abc's into account (maybe via a predefined function)?



    If the abstract base class's module has not been imported, it may not
    even be loaded into memory, even though it is technically considered a
    superclass. Consider this:


    Python 2.7.1 (r271:86832, Nov 27 2010, 18:30:46) [MSC v.1500 32 bit
    (Intel)] on win32
    Type "help", "copyright", "credits" or "license" for more information.
    >>> def common_base(classes):

    .... common = set()
    .... for cls in object.__subclasses__():
    .... if all(issubclass(c, cls) for c in classes):
    .... common.add(cls)
    .... return common
    ....
    >>> common_base([int, float])

    set([<class '_abcoll.Hashable'>])
    >>> import numbers
    >>> common_base([int, float])

    set([<class 'numbers.Number'>, <class '_abcoll.Hashable'>])


    If you're okay with that, then the approach above might work.


    > while len(common) > 1:
    > cls1 = common.pop()
    > cls2 = common.pop()
    > if issubclass(cls1, cls2):
    > common.add(cls1)
    > elif issubclass(cls2, cls1):
    > common.add(cls2)


    There is a flaw with your set reduction code here. If neither class
    is a subclass of the other, then both will be removed. There may not
    actually be a single closest common base class, however. What would
    you expect the function to return in the following situation?

    class A(object): pass
    class B(object): pass
    class C(A, B): pass
    class D(A, B): pass

    print common_base([C, D])
     
    Ian Kelly, Jan 25, 2013
    #2
    1. Advertising

  3. lars van gemerden

    Peter Otten Guest

    lars van gemerden wrote:

    > Hi all,
    >
    > i was writing a function to determine the common base class of a number
    > classes:
    >
    > def common_base(classes):
    > if not len(classes):
    > return None
    > common = set(classes.pop().mro())
    > for cls in classes:
    > common.intersection_update(cls.mro())
    > while len(common) > 1:
    > cls1 = common.pop()
    > cls2 = common.pop()
    > if issubclass(cls1, cls2):
    > common.add(cls1)
    > elif issubclass(cls2, cls1):
    > common.add(cls2)
    > return common.pop()
    >
    > and ran common_base(int, float), hoping to get numbers.Number.
    >
    > this did not work because abstract base classes are not always in the
    > mro() of classes.
    >
    > My question is: is there a way to obtain the abc's of a class or otherwise
    > a way to make the function above take abc's into account (maybe via a
    > predefined function)?


    The abstract base classes may run arbitrary code to determine the subclass
    relationship:

    >>> from abc import ABCMeta
    >>> import random
    >>> class Maybe(metaclass=ABCMeta):

    .... @classmethod
    .... def __subclasshook__(cls, C):
    .... print("processing", C)
    .... return random.choice((False, True))
    ....
    >>> isinstance(1.1, Maybe)

    processing <class 'float'>
    True
    >>> isinstance(1.1, Maybe)

    True
    >>> isinstance(1, Maybe)

    processing <class 'int'>
    False
    >>> issubclass(float, Maybe)

    True

    You'd have to check every pair of classes explicitly and might still miss
    (for example) numbers.Number as the module may not have been imported.

    I think you are out of luck.
     
    Peter Otten, Jan 25, 2013
    #3
  4. On Friday, January 25, 2013 8:04:32 PM UTC+1, Ian wrote:
    > On Fri, Jan 25, 2013 at 10:40 AM, lars van gemerden
    >
    > <> wrote:
    >
    > > Hi all,

    >
    > >

    >
    > > i was writing a function to determine the common base class of a number classes:

    >
    > >

    >
    > [...]
    >
    > >

    >
    > > and ran common_base(int, float), hoping to get numbers.Number.

    >
    > >

    >
    > > this did not work because abstract base classes are not always in the mro() of classes.

    >
    > >

    >
    > > My question is: is there a way to obtain the abc's of a class or otherwise a way to make the function above take abc's into account (maybe via a predefined function)?

    >
    >
    >
    >
    >
    > If the abstract base class's module has not been imported, it may not
    >
    > even be loaded into memory, even though it is technically considered a
    >
    > superclass. Consider this:
    >
    >
    >
    >
    >
    > Python 2.7.1 (r271:86832, Nov 27 2010, 18:30:46) [MSC v.1500 32 bit
    >
    > (Intel)] on win32
    >
    > Type "help", "copyright", "credits" or "license" for more information.
    >
    > >>> def common_base(classes):

    >
    > ... common = set()
    >
    > ... for cls in object.__subclasses__():
    >
    > ... if all(issubclass(c, cls) for c in classes):
    >
    > ... common.add(cls)
    >
    > ... return common
    >
    > ...
    >
    > >>> common_base([int, float])

    >
    > set([<class '_abcoll.Hashable'>])
    >
    > >>> import numbers

    >
    > >>> common_base([int, float])

    >
    > set([<class 'numbers.Number'>, <class '_abcoll.Hashable'>])
    >
    >
    >
    >
    >
    > If you're okay with that, then the approach above might work.
    >
    >
    >
    >
    >
    > > while len(common) > 1:

    >
    > > cls1 = common.pop()

    >
    > > cls2 = common.pop()

    >
    > > if issubclass(cls1, cls2):

    >
    > > common.add(cls1)

    >
    > > elif issubclass(cls2, cls1):

    >
    > > common.add(cls2)

    >
    >
    >
    > There is a flaw with your set reduction code here. If neither class
    >
    > is a subclass of the other, then both will be removed. There may not
    >
    > actually be a single closest common base class, however. What would
    >
    > you expect the function to return in the following situation?
    >
    >
    >
    > class A(object): pass
    >
    > class B(object): pass
    >
    > class C(A, B): pass
    >
    > class D(A, B): pass
    >
    >
    >
    > print common_base([C, D])


    thanks, good catch, and very concise answer. I'll give up on trying to get abc's and improve my algorithm.
     
    lars van gemerden, Jan 25, 2013
    #4
  5. On Friday, January 25, 2013 8:04:32 PM UTC+1, Ian wrote:
    > On Fri, Jan 25, 2013 at 10:40 AM, lars van gemerden
    >
    > <> wrote:
    >
    > > Hi all,

    >
    > >

    >
    > > i was writing a function to determine the common base class of a number classes:

    >
    > >

    >
    > [...]
    >
    > >

    >
    > > and ran common_base(int, float), hoping to get numbers.Number.

    >
    > >

    >
    > > this did not work because abstract base classes are not always in the mro() of classes.

    >
    > >

    >
    > > My question is: is there a way to obtain the abc's of a class or otherwise a way to make the function above take abc's into account (maybe via a predefined function)?

    >
    >
    >
    >
    >
    > If the abstract base class's module has not been imported, it may not
    >
    > even be loaded into memory, even though it is technically considered a
    >
    > superclass. Consider this:
    >
    >
    >
    >
    >
    > Python 2.7.1 (r271:86832, Nov 27 2010, 18:30:46) [MSC v.1500 32 bit
    >
    > (Intel)] on win32
    >
    > Type "help", "copyright", "credits" or "license" for more information.
    >
    > >>> def common_base(classes):

    >
    > ... common = set()
    >
    > ... for cls in object.__subclasses__():
    >
    > ... if all(issubclass(c, cls) for c in classes):
    >
    > ... common.add(cls)
    >
    > ... return common
    >
    > ...
    >
    > >>> common_base([int, float])

    >
    > set([<class '_abcoll.Hashable'>])
    >
    > >>> import numbers

    >
    > >>> common_base([int, float])

    >
    > set([<class 'numbers.Number'>, <class '_abcoll.Hashable'>])
    >
    >
    >
    >
    >
    > If you're okay with that, then the approach above might work.
    >
    >
    >
    >
    >
    > > while len(common) > 1:

    >
    > > cls1 = common.pop()

    >
    > > cls2 = common.pop()

    >
    > > if issubclass(cls1, cls2):

    >
    > > common.add(cls1)

    >
    > > elif issubclass(cls2, cls1):

    >
    > > common.add(cls2)

    >
    >
    >
    > There is a flaw with your set reduction code here. If neither class
    >
    > is a subclass of the other, then both will be removed. There may not
    >
    > actually be a single closest common base class, however. What would
    >
    > you expect the function to return in the following situation?
    >
    >
    >
    > class A(object): pass
    >
    > class B(object): pass
    >
    > class C(A, B): pass
    >
    > class D(A, B): pass
    >
    >
    >
    > print common_base([C, D])


    thanks, good catch, and very concise answer. I'll give up on trying to get abc's and improve my algorithm.
     
    lars van gemerden, Jan 25, 2013
    #5
  6. On Friday, January 25, 2013 8:08:18 PM UTC+1, Peter Otten wrote:
    > lars van gemerden wrote:
    >
    >
    >
    > > Hi all,

    >
    > >

    >
    > > i was writing a function to determine the common base class of a number

    >
    > > classes:

    >
    > >

    >
    > > def common_base(classes):

    >
    > > if not len(classes):

    >
    > > return None

    >
    > > common = set(classes.pop().mro())

    >
    > > for cls in classes:

    >
    > > common.intersection_update(cls.mro())

    >
    > > while len(common) > 1:

    >
    > > cls1 = common.pop()

    >
    > > cls2 = common.pop()

    >
    > > if issubclass(cls1, cls2):

    >
    > > common.add(cls1)

    >
    > > elif issubclass(cls2, cls1):

    >
    > > common.add(cls2)

    >
    > > return common.pop()

    >
    > >

    >
    > > and ran common_base(int, float), hoping to get numbers.Number.

    >
    > >

    >
    > > this did not work because abstract base classes are not always in the

    >
    > > mro() of classes.

    >
    > >

    >
    > > My question is: is there a way to obtain the abc's of a class or otherwise

    >
    > > a way to make the function above take abc's into account (maybe via a

    >
    > > predefined function)?

    >
    >
    >
    > The abstract base classes may run arbitrary code to determine the subclass
    >
    > relationship:
    >
    >
    >
    > >>> from abc import ABCMeta

    >
    > >>> import random

    >
    > >>> class Maybe(metaclass=ABCMeta):

    >
    > ... @classmethod
    >
    > ... def __subclasshook__(cls, C):
    >
    > ... print("processing", C)
    >
    > ... return random.choice((False, True))
    >
    > ...
    >
    > >>> isinstance(1.1, Maybe)

    >
    > processing <class 'float'>
    >
    > True
    >
    > >>> isinstance(1.1, Maybe)

    >
    > True
    >
    > >>> isinstance(1, Maybe)

    >
    > processing <class 'int'>
    >
    > False
    >
    > >>> issubclass(float, Maybe)

    >
    > True
    >
    >
    >
    > You'd have to check every pair of classes explicitly and might still miss
    >
    > (for example) numbers.Number as the module may not have been imported.
    >
    >
    >
    > I think you are out of luck.


    Thank you, interesting example. Added confirmation that trying to get the abc's is a bad idea.
     
    lars van gemerden, Jan 25, 2013
    #6
  7. On Friday, January 25, 2013 8:08:18 PM UTC+1, Peter Otten wrote:
    > lars van gemerden wrote:
    >
    >
    >
    > > Hi all,

    >
    > >

    >
    > > i was writing a function to determine the common base class of a number

    >
    > > classes:

    >
    > >

    >
    > > def common_base(classes):

    >
    > > if not len(classes):

    >
    > > return None

    >
    > > common = set(classes.pop().mro())

    >
    > > for cls in classes:

    >
    > > common.intersection_update(cls.mro())

    >
    > > while len(common) > 1:

    >
    > > cls1 = common.pop()

    >
    > > cls2 = common.pop()

    >
    > > if issubclass(cls1, cls2):

    >
    > > common.add(cls1)

    >
    > > elif issubclass(cls2, cls1):

    >
    > > common.add(cls2)

    >
    > > return common.pop()

    >
    > >

    >
    > > and ran common_base(int, float), hoping to get numbers.Number.

    >
    > >

    >
    > > this did not work because abstract base classes are not always in the

    >
    > > mro() of classes.

    >
    > >

    >
    > > My question is: is there a way to obtain the abc's of a class or otherwise

    >
    > > a way to make the function above take abc's into account (maybe via a

    >
    > > predefined function)?

    >
    >
    >
    > The abstract base classes may run arbitrary code to determine the subclass
    >
    > relationship:
    >
    >
    >
    > >>> from abc import ABCMeta

    >
    > >>> import random

    >
    > >>> class Maybe(metaclass=ABCMeta):

    >
    > ... @classmethod
    >
    > ... def __subclasshook__(cls, C):
    >
    > ... print("processing", C)
    >
    > ... return random.choice((False, True))
    >
    > ...
    >
    > >>> isinstance(1.1, Maybe)

    >
    > processing <class 'float'>
    >
    > True
    >
    > >>> isinstance(1.1, Maybe)

    >
    > True
    >
    > >>> isinstance(1, Maybe)

    >
    > processing <class 'int'>
    >
    > False
    >
    > >>> issubclass(float, Maybe)

    >
    > True
    >
    >
    >
    > You'd have to check every pair of classes explicitly and might still miss
    >
    > (for example) numbers.Number as the module may not have been imported.
    >
    >
    >
    > I think you are out of luck.


    Thank you, interesting example. Added confirmation that trying to get the abc's is a bad idea.
     
    lars van gemerden, Jan 25, 2013
    #7
  8. for future reference, i decided to go with 2 functions:

    def common_bases(classes):
    if not len(classes):
    return None
    common = set(classes.pop().mro())
    for cls in classes:
    common.intersection_update(cls.mro()) #all subclasses in common
    return [cls for cls in common if not any(sub in common for sub in cls.__subclasses__())] #the classes of which no subclasses are present

    def unique_common_base(classes):
    while len(classes) > 1:
    classes = common_bases(classes)
    return classes.pop()

    if i tested and understood correctly, they only take classes in the mro() into account (which might include abc's), the first gives all common base classes, the second recursively reduces further to one single class (the latter might not make to much sense, but in my program it is a safe bet for rare cases).

    Thanks again for the help,

    Cheers, Lars
     
    lars van gemerden, Jan 26, 2013
    #8
  9. for future reference, i decided to go with 2 functions:

    def common_bases(classes):
    if not len(classes):
    return None
    common = set(classes.pop().mro())
    for cls in classes:
    common.intersection_update(cls.mro()) #all subclasses in common
    return [cls for cls in common if not any(sub in common for sub in cls.__subclasses__())] #the classes of which no subclasses are present

    def unique_common_base(classes):
    while len(classes) > 1:
    classes = common_bases(classes)
    return classes.pop()

    if i tested and understood correctly, they only take classes in the mro() into account (which might include abc's), the first gives all common base classes, the second recursively reduces further to one single class (the latter might not make to much sense, but in my program it is a safe bet for rare cases).

    Thanks again for the help,

    Cheers, Lars
     
    lars van gemerden, Jan 26, 2013
    #9
    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. Jiong Feng
    Replies:
    0
    Views:
    877
    Jiong Feng
    Nov 19, 2003
  2. Bruce Sam
    Replies:
    15
    Views:
    7,985
    John C. Bollinger
    Nov 19, 2004
  3. vsgdp

    ABC inheriting from ABC

    vsgdp, Sep 24, 2005, in forum: C++
    Replies:
    1
    Views:
    316
    vsgdp
    Sep 24, 2005
  4. Gunter Henriksen

    x.abc vs x['abc']

    Gunter Henriksen, May 13, 2009, in forum: Python
    Replies:
    1
    Views:
    361
    alex23
    May 15, 2009
  5. Replies:
    4
    Views:
    159
Loading...

Share This Page