简体   繁体   中英

Python multiple inheritance of __new__ and __init__ with a string and second class

I'm trying to create a derived class that inherits from both a str type and a second class. It's problematic since the str type doesn't simply call __init__ , but the __new__ method due to its immutability. I know that for __init__ and super to work well, you need to have the same calling structure all the way down. However the following implementation fails:

class base(object):
    def __new__(cls, *args, **kwargs):
        print "NEW  BASE:", cls, args, kwargs
        return super(base, cls).__new__(cls, *args, **kwargs)

    def __init__(self, *args, **kwargs):
        print "INIT BASE", args, kwargs

class foo(base, str):
    def __new__(cls, *args, **kwargs):
        return super(foo, cls).__new__(cls, *args, **kwargs)

    def __init__(self, *args, **kwargs):
        super(foo, self).__init__(*args, **kwargs)

Here foo('cat') works with:

>> NEW  BASE: <class '__main__.foo'> ('cat',) {}
>> INIT BASE ('cat',) {}

but with an argument foo('cat', x=3) , it fails:

>> NEW  BASE: <class '__main__.foo'> ('cat',) {'x': 3}
Traceback (most recent call last):
  File "inh.py", line 19, in <module>
    foo('cat', x=3)
  File "inh.py", line 12, in __new__
    return super(foo, cls).__new__(cls, *args, **kwargs)
  File "inh.py", line 4, in __new__
    return super(base, cls).__new__(cls, *args, **kwargs)
TypeError: str() takes at most 1 argument (2 given)

I can get this to work by changing the base.__new__ method to:

def __new__(cls, *args, **kwargs):
    return super(base, cls).__new__(cls)

but now I've changed the calling structure, which I feel will cause me problems later down the line.

How do I properly inherit from a string and a second class ?

You can't just do

def __new__(cls, *args, **kwargs):
    return super(base, cls).__new__(cls)

because this will cause incorrect call for new of str (you will not pass allowed argument

>>> foo('t')
NEW  BASE: <class '__main__.foo'> ('t',) {}
INIT BASE ('t',) {}
''

You should do something like

def __new__(cls, *args, **kwargs):
    return super(base, cls).__new__(cls, *args[:1])

But this can broken something if You will use base class as mixin for class which __new__ method accept more than one argument.

as a option maybe You should have class inherited from str but with overridden new method:

class CarelessStr(str):
    def __new__(cls, *args, **kwargs):
        return super(CarelessStr, cls).__new__(cls, *args[:1])

class foo(base, CarelessStr):
    def __new__(cls, *args, **kwargs):
        return super(foo, cls).__new__(cls, *args, **kwargs)

    def __init__(self, *args, **kwargs):
        super(foo, self).__init__(*args, **kwargs)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM