unbound method must be called with X instance as f

2019-05-27 10:06发布

问题:

I get the following error message in my Python 2-Code (2.7.9): TypeError: unbound method issueTransitionJIRA() must be called with AccessCd instance as first argument (got AccessCd instance instead)

So python complains that the 1st argument should be the class instance which I actually passed..?! Certainly I'm missing something, but I did not get it yet, searching other answers on similar error messages... Can anyone help/explain me? Thanks a lot!

Here is my code structure:

main-file w32AccessCd.py:

import transitionStatusTracking

class AccessCd:
    def __init__(self):
        # ...
        self.main()

    def main():
        tST = transitionStatusTracking.tStateTrack(self)
        tST.setup()

    # ...

    def issueTransitionJ(self, issuekey, workflowcmd):
        # ...
        return True     


accesscd = AccessCd()

module-file transitionStatusTracking.py:

from w32AccessCd import AccessCd

class tStateTrack:

    def __init__(self, ACCESSCD):
        self.ACCESSCD = ACCESSCD

    def setup(self):
        AccessCd.issueTransitionJ(self.ACCESSCD, self.key, "error")

Error:

TypeError: unbound method issueTransitionJ() must be called with AccessCd instance as first argument (got AccessCd instance instead)

回答1:

When Python runs a script, the file is stored in the __main__ namespace. Importing it as a module, on the other hand, puts it in the <name-of-module> namespace.

You are running w32AccessCd.py as the main script, defining the __main__.AccessCd.

You then import transitionStatusTracking, which turns around and imports the w32AccessCd. This creates a second copy of that module, as the w32AccessCd namespace. You now also have w32AccessCd.AccessCd. This is a separate class, and is distinct from the other class.

What happens next is this:

  • accesscd = AccessCd() creates an instance of type __main__.AccessCd.
  • __main__.AccessCd.__init__ calls __main__.AccessCd.main.
  • __main__.AccessCd.main creates an instance of transitionStatusTracking.tStateTrack, passing in the __main__.AccessCd instance in.
  • You call the transitionStatusTracking.tStateTrack.setup method, which tries to use the w32AccessCd.AccessCd.issueTransitionJ() method with a __main__.AccessCd instance, and the call fails.

You have three options to fix this:

  • In the transitionStatusTracking module, import from __main__:

    from __main__ import AccessCd
    

    and to make the circular import work, you'd have to move the import transitionStatusTracking line to below the class AccessCd block, otherwise the class is not defined yet. Alternatively use import __main__ and reference __main__.AccessCd in your code.

    Now you have the same class.

  • Create a third file to do the scripting; say main.py, with:

    from w32AccessCd import AccessCd
    accesscd = AccessCd()
    

    and remove the accesscd = AcessCd() line from the w32AccessCd module. Now there will be just the w32AccessCd.AccessCd class instance being passed around.

  • Call the method directly rather than unbound:

    def setup(self):
        self.ACCESSCD.issueTransitionJ(self.key, "error")
    

    This ensures the method is bound to the right instance and it no longer matters where the class came from.