简体   繁体   中英

How to add a named keyword argument to a function signature

Can I use a mixin class to add a named keyword to the signature of a function in the base? At the moment I can't work out how to avoid overwriting the base function's signature:

from inspect import signature

class Base(object):
    def __init__(self, foo='bar', **kwargs):
        pass

class Mixin(base):
    def __init__(self, foo2='bar2', **kwargs):
        super(Mixin, self).__init__(**kwargs)

class P(Mixin, Base):
    pass

print(signature(P.__init__))


# Output: (self, foo2='bar2', **kwargs)

$ Desired output: (self, foo1='bar1', foo2='bar2', **kwargs)

Edit Thanks for the answers so far, unfortunately I actually need to add the named parameter to the function signature, while also keeping the named parameters from the original base function signature (and these will vary depending on the base). The reason is that the signature is used elsewhere for intraspection to extract the parameter names. : Is this going to be possible in the __init__ method?

Here is a (horrible) partial solution, which changes the signature on instances, but not on the class itself, also it's missing **kwargs for some reason:

class Mixin(Base):
     def __init__(self, foo2='bar2', **kwargs):
         super(Mixin, self).__init__(**kwargs)
         sig = signature(super(Mixin, self).__init__)
         params = {k:v.default for k,v in sig.parameters.items() if v.default != _empty}
         params['foo2'] = 'bar2'
         argstring = ",".join("{}='{}'".format(k,v) for k,v in params.items())
         exec("def new_init({}, **kwargs): self.__init__(**kwargs)".format(argstring))
         self.__init__ = new_init


class P(Mixin, Base):
    pass

p = P()
print(signature(p.__init__))
# (foo2='bar2', foo='bar')

When subclassing, you can either extend or override the methods of the superclass, but you can't* modify them directly. Overiding is achieved by simply writing a new method of the same name; extension is achieved by overriding the method and then calling the superclass's method as a part of your replacement.

Here are two classes, the second of which overrides one method and extends another:

class A:
    def method1(self):
        print("Method A.1 called")
    def method2(self, arg1):
        print("Method A.2 called with argument", arg1)

class B(A):
    def method1(self):
        print("Method B.1 called")
    def method2(self, arg1, arg2="keyword arg"):
        print("Method B.1 called with arg1=", arg1, "arg2=", arg2)
        super().method2(arg1)
        print("Extended method complete")

Using this in an interactive context we see the following:

>>> b = B()
>>> b.method1()
Method B.1 called
>>> b.method2("first", arg2="second")
Method B.1 called with arg1= first arg2= second
Method A.2 called with argument first
Extended method complete

* Technically it's possible in Python, but the code would be ugly and anyway any changes to the superclass would be seen by everything that used the superclass.

@jonrsharpe answered this in the comment, but if you want to require a parameter you could do this as a dynamic check in the mixin's method:

class Base(object):
    def __init__(self, foo='bar', **kwargs):
        pass

class Mixin(base):
    def __init__(self, **kwargs):
        kwargs.pop('required_param')
        kwargs.pop('optional_param', None)
        super(Mixin, self).__init__(**kwargs)

class P(Mixin, Base):
    pass

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