admin管理员组

文章数量:1401241

I'm trying to use two classes, A and B, as mixing classes of AB. They both have __init_subclass__ methods. The problem is that both __init__sublass__ methods have the same argument msg. Therefore I've used an adaptor class B_ to rename B's argument msg_b. But I'm having trouble!

The nearest I have got is:

class A:
    def __init_subclass__(cls, msg, **kwargs):
        print(f'{cls=} A: {msg}')
        super().__init_subclass__(**kwargs)

class B:
    def __init_subclass__(cls, msg, **kwargs):
        print(f'{cls=} B: {msg}')
        super().__init_subclass__(**kwargs)

# Adaptor class  `B_` needed because both `A` and `B` have an argument `msg`.
class B_:
    # Rename `msg` to `msg_b`.
    def __init_subclass__(cls, msg_b, **kwargs):
        # `B.__init_subclass__(msg_b, **kwargs)` sets the subclass as `B` not `cls`, but otherwise works.
        B.__init_subclass__.__func__(cls, msg=msg_b, **kwargs)

    # Still need a `B`.
    def __init__(self, *args, **kwargs):
        self.b = B(*args, **kwargs)

    # Forward all the attributes to `self.b`.
    def __getattr__(self, item):
        return getattr(self.b, item)

class AB(A, B_, msg='Hello.', msg_b='Also, hello.' ):
    ...

print(f'{AB()=}, {isinstance(AB(), A)=}, {isinstance(AB(), B_)=}, {isinstance(AB(), B)=}')

Which does call both __init_sublass__s with the correct class argument, it prints:

cls=<class '__main__.AB'> A: Hello.
cls=<class '__main__.AB'> B: Also, hello.

But then you get the error:

Traceback (most recent call last):
  File <definition of AB>, in <module>
    class AB(A, B_, msg='Hello.', msg_b='Also, hello.' ):
  File <A's>, in __init_subclass__
    super().__init_subclass__(**kwargs)
  File <B_'s>, in __init_subclass__
    B.__init_subclass__.__func__(cls, msg=msg_b, **kwargs)
  File <B's>, in __init_subclass__
    super().__init_subclass__(**kwargs)
    ^^^^^^^^^^^^^^^^^^^^^^^^^
TypeError: super(type, obj): obj must be an instance or subtype of type

Presumably because B is not a super type of AB (B_ is). I’m not clear why super wants subtypes matching though!

Any ideas on how to fix this?

I'm trying to use two classes, A and B, as mixing classes of AB. They both have __init_subclass__ methods. The problem is that both __init__sublass__ methods have the same argument msg. Therefore I've used an adaptor class B_ to rename B's argument msg_b. But I'm having trouble!

The nearest I have got is:

class A:
    def __init_subclass__(cls, msg, **kwargs):
        print(f'{cls=} A: {msg}')
        super().__init_subclass__(**kwargs)

class B:
    def __init_subclass__(cls, msg, **kwargs):
        print(f'{cls=} B: {msg}')
        super().__init_subclass__(**kwargs)

# Adaptor class  `B_` needed because both `A` and `B` have an argument `msg`.
class B_:
    # Rename `msg` to `msg_b`.
    def __init_subclass__(cls, msg_b, **kwargs):
        # `B.__init_subclass__(msg_b, **kwargs)` sets the subclass as `B` not `cls`, but otherwise works.
        B.__init_subclass__.__func__(cls, msg=msg_b, **kwargs)

    # Still need a `B`.
    def __init__(self, *args, **kwargs):
        self.b = B(*args, **kwargs)

    # Forward all the attributes to `self.b`.
    def __getattr__(self, item):
        return getattr(self.b, item)

class AB(A, B_, msg='Hello.', msg_b='Also, hello.' ):
    ...

print(f'{AB()=}, {isinstance(AB(), A)=}, {isinstance(AB(), B_)=}, {isinstance(AB(), B)=}')

Which does call both __init_sublass__s with the correct class argument, it prints:

cls=<class '__main__.AB'> A: Hello.
cls=<class '__main__.AB'> B: Also, hello.

But then you get the error:

Traceback (most recent call last):
  File <definition of AB>, in <module>
    class AB(A, B_, msg='Hello.', msg_b='Also, hello.' ):
  File <A's>, in __init_subclass__
    super().__init_subclass__(**kwargs)
  File <B_'s>, in __init_subclass__
    B.__init_subclass__.__func__(cls, msg=msg_b, **kwargs)
  File <B's>, in __init_subclass__
    super().__init_subclass__(**kwargs)
    ^^^^^^^^^^^^^^^^^^^^^^^^^
TypeError: super(type, obj): obj must be an instance or subtype of type

Presumably because B is not a super type of AB (B_ is). I’m not clear why super wants subtypes matching though!

Any ideas on how to fix this?

Share Improve this question asked Mar 24 at 4:52 Howard LovattHoward Lovatt 1,0301 gold badge9 silver badges15 bronze badges 8
  • super() finds the next class in the MRO but in your case B is not in the MRO, hence the error. You may patch the MRO but I'm not sure that's the correct thing to do. – Weijun Zhou Commented Mar 24 at 5:32
  • @WeijunZhou The super of B is object. So why doesn't super find that? Why does it care that B is not the super of AB? Note the error comes from the super call in B, not AB. – Howard Lovatt Commented Mar 24 at 6:21
  • 1 super() is a function to call the next method in a MRO. It does not mean the parent class. As long as your class is not part of the MRO it cannot possibly work. The MRO contains A, B_, object. No B is there. – Weijun Zhou Commented Mar 24 at 6:30
  • 1 It's similar to this. It doesn't matter that both A and B are directly inherited from object. More precisely, super() returns a proxy object that helps to navigate the MRO and invokes the methods in turn. – Weijun Zhou Commented Mar 24 at 6:39
  • @WeijunZhou the next object of B in the MRO is object - it is the only thing it inherites from. – Howard Lovatt Commented Mar 24 at 7:17
 |  Show 3 more comments

1 Answer 1

Reset to default 1

Presumably you don't have the authorization to modify A or B, or you would've renamed the msg parameter in one of their __init_subclass__s already.

One workaround then is to use an intermediary base class between A and B with an __init_subclass__ method that renames the argument msg_b to msg before passing the baton to B.__init_subclass__:

class ABGlue:
    def __init_subclass__(cls, msg_b, **kwargs):
        super().__init_subclass__(msg=msg_b, **kwargs)

class AB(A, ABGlue, B, msg='Hello.', msg_b='Also, hello.' ):
    ...

print(f'{AB()=}')
print(f'{isinstance(AB(), A)=}')
print(f'{isinstance(AB(), B)=}')

This outputs something like:

cls=<class '__main__.AB'> A: Hello.
cls=<class '__main__.AB'> B: Also, hello.
AB()=<__main__.AB object at 0x14a498119460>
isinstance(AB(), A)=True
isinstance(AB(), B)=True

Demo: https://ideone/soXBWW

本文标签: pythonProblem if two inherited initsubclasss have the same argument nameStack Overflow