Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > comp.lang.python > #37682 > unrolled thread

finding abc's

Started bylars van gemerden <lars@rational-it.com>
First post2013-01-25 09:40 -0800
Last post2013-01-25 12:08 -0800
Articles 9 — 3 participants

Back to article view | Back to comp.lang.python


Contents

  finding abc's lars van gemerden <lars@rational-it.com> - 2013-01-25 09:40 -0800
    Re: finding abc's Ian Kelly <ian.g.kelly@gmail.com> - 2013-01-25 12:04 -0700
      Re: finding abc's lars van gemerden <lars@rational-it.com> - 2013-01-25 12:05 -0800
      Re: finding abc's lars van gemerden <lars@rational-it.com> - 2013-01-25 12:05 -0800
    Re: finding abc's Peter Otten <__peter__@web.de> - 2013-01-25 20:08 +0100
      Re: finding abc's lars van gemerden <lars@rational-it.com> - 2013-01-25 12:08 -0800
        Re: finding abc's lars van gemerden <lars@rational-it.com> - 2013-01-25 16:48 -0800
        Re: finding abc's lars van gemerden <lars@rational-it.com> - 2013-01-25 16:48 -0800
      Re: finding abc's lars van gemerden <lars@rational-it.com> - 2013-01-25 12:08 -0800

#37682 — finding abc's

Fromlars van gemerden <lars@rational-it.com>
Date2013-01-25 09:40 -0800
Subjectfinding abc's
Message-ID<766ec7eb-ab43-4c17-8073-3a0e6a8b89ea@googlegroups.com>
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 

[toc] | [next] | [standalone]


#37687

FromIan Kelly <ian.g.kelly@gmail.com>
Date2013-01-25 12:04 -0700
Message-ID<mailman.1051.1359140711.2939.python-list@python.org>
In reply to#37682
On Fri, Jan 25, 2013 at 10:40 AM, lars van gemerden
<lars@rational-it.com> 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])

[toc] | [prev] | [next] | [standalone]


#37690

Fromlars van gemerden <lars@rational-it.com>
Date2013-01-25 12:05 -0800
Message-ID<9ac2bf34-1945-4819-9bd5-c0a42179fd00@googlegroups.com>
In reply to#37687
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
> 
> <lars@rational-it.com> 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.

[toc] | [prev] | [next] | [standalone]


#37691

Fromlars van gemerden <lars@rational-it.com>
Date2013-01-25 12:05 -0800
Message-ID<mailman.1054.1359144344.2939.python-list@python.org>
In reply to#37687
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
> 
> <lars@rational-it.com> 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.

[toc] | [prev] | [next] | [standalone]


#37688

FromPeter Otten <__peter__@web.de>
Date2013-01-25 20:08 +0100
Message-ID<mailman.1052.1359140888.2939.python-list@python.org>
In reply to#37682
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.

[toc] | [prev] | [next] | [standalone]


#37692

Fromlars van gemerden <lars@rational-it.com>
Date2013-01-25 12:08 -0800
Message-ID<0d837f4c-c4a7-4df6-978c-b62a10ef9d70@googlegroups.com>
In reply to#37688
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. 

[toc] | [prev] | [next] | [standalone]


#37705

Fromlars van gemerden <lars@rational-it.com>
Date2013-01-25 16:48 -0800
Message-ID<c84c1e15-591b-4faa-bb8b-708c7e006bf1@googlegroups.com>
In reply to#37692
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

[toc] | [prev] | [next] | [standalone]


#37706

Fromlars van gemerden <lars@rational-it.com>
Date2013-01-25 16:48 -0800
Message-ID<mailman.1066.1359161309.2939.python-list@python.org>
In reply to#37692
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

[toc] | [prev] | [next] | [standalone]


#37693

Fromlars van gemerden <lars@rational-it.com>
Date2013-01-25 12:08 -0800
Message-ID<mailman.1055.1359144501.2939.python-list@python.org>
In reply to#37688
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. 

[toc] | [prev] | [standalone]


Back to top | Article view | comp.lang.python


csiph-web