finding abc's

L

lars van gemerden

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
 
I

Ian Kelly

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..... 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([ said:
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])
 
P

Peter Otten

lars said:
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:
.... @classmethod
.... def __subclasshook__(cls, C):
.... print("processing", C)
.... return random.choice((False, True))
....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.
 
L

lars van gemerden

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.

... 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([ said:
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):

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.
 
L

lars van gemerden

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.

... 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([ said:
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):

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.
 
L

lars van gemerden

lars van gemerden wrote:
















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

relationship:




... @classmethod

... def __subclasshook__(cls, C):

... print("processing", C)

... return random.choice((False, True))

...


processing <class 'float'>

True


processing <class 'int'>

False


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.
 
L

lars van gemerden

lars van gemerden wrote:
















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

relationship:




... @classmethod

... def __subclasshook__(cls, C):

... print("processing", C)

... return random.choice((False, True))

...


processing <class 'float'>

True


processing <class 'int'>

False


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.
 
L

lars van gemerden

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
 
L

lars van gemerden

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
 

Ask a Question

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

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,580
Members
45,054
Latest member
TrimKetoBoost

Latest Threads

Top