POO Python: 1 positional argument but 2 were given

2

I do an exercise in Python the intention is to move around a 5x5 space but I have an error in the simulation of a switch that they think is:

class Robot():


    def __init__(self, x, y):
        self.x = x
        self.y = y
        switcher = {}

    def LE(self):
        if (self.x == 1):
            return "You are in the left limit"
        else:
            self.x -= 1
            return "({},{})".format(self.x, self.y)

    def RI(self):
        if (self.x == 5):
            return "You are in the rigth limit"
        else:
            self.x += 1
            return "({},{})".format(self.x, self.y)

    def UP(self):
        if (self.y == 1):
            return "You are in the up limit"
        else:
            self.y -= 1
            return "({},{})".format(self.x, self.y)

    def DO(self):
        if (self.y == 5):
            return "You are in the down limit"
        else:
            self.y += 1
            return "({},{})".format(self.x, self.y)

    switcher = {
       "le": LE,
       "ri": RI,
       "up": UP,
       "do": DO
    }

    def move(argumen):
        func = switcher.get(argumen, "Invalid Argument")
        return func()

def main():
   player = Robot(1, 1)
   player.move("ri")


if __name__ == "__main__":
   main()

The error in mention is the following:

  

Traceback (most recent call last):

     

File "E: \ Documents \ robot.py", line 54, in       main ()

     

File "E: \ Documents \ robot.py", line 50, in main       player.move ("ri")

     

TypeError: move () takes 1 positional argument but 2 were given

    
asked by Alexander Camacho 28.08.2018 в 19:39
source

3 answers

1

Your code has several errors:

  • You are thinking that the switcher = {"le": LE, ...} is the same as switcher = {} declared in the constructor, in the first case it is a static or variable attribute of the class, and in the second it is a local variable that is not used and therefore will be removed.

  • The function move(...) is a method of the class so it must have the first parameter to self.

Assuming you want to continue using the static attribute, you should use the following:

def move(self, argumen):
    func = Robot.switcher.get(argumen, "Invalid Argument")
    return func(self)

As you can see to access the switcher you must use the name of the class (you could also use self: self.switcher but using the name of the class makes the code more readable) and to evaluate the method you must pass the instance.

Note: remove switcher = {} from the constructor, it is unnecessary.

I see that your intention is to use the functions by means of the name of the function, a better solution is to use getattr(...) , for it changes the names of the functions to the values of the dictionary keys:

class Robot():
    def __init__(self, x, y):
        self.x = x
        self.y = y

    def le(self):
        if self.x == 1:
            return "You are in the left limit"
        else:
            self.x -= 1
            return "({},{})".format(self.x, self.y)

    def ri(self):
        if self.x == 5:
            return "You are in the rigth limit"
        else:
            self.x += 1
            return "({},{})".format(self.x, self.y)

    def up(self):
        if self.y == 1:
            return "You are in the up limit"
        else:
            self.y -= 1
            return "({},{})".format(self.x, self.y)

    def do(self):
        if self.y == 5:
            return "You are in the down limit"
        else:
            self.y += 1
            return "({},{})".format(self.x, self.y)

    def move(self, argumen):
        return getattr(self, argumen)()

def main():
   player = Robot(1, 1)
   print(player.move("ri"))


if __name__ == "__main__":
   main()
    
answered by 28.08.2018 / 20:27
source
1

In addition to the corrections to your code that you have given in the other answers, I want to emphasize that when you do object-oriented programming in python, it is important to remember the following:

When invoking a method of an object using the syntax objeto.metodo() , the method in question always receives the object in question as the first parameter, in addition to all the other parameters that you want to pass to it.

This implies more things to take into account and where it is easy to be wrong (sooner or later we all "bite" one of these problems, me especially the third):

  • When you declare that method within the class, you should always write an additional parameter to the ones you want it to receive. This parameter must be the first, and is typically called self (although you could really call it what you want, it is better to stick to the rule). It represents the object on which you are operating, and is equivalent to this of java, although in python it is mandatory to declare it as the first parameter.

  • When you access an attribute of the object from one of its methods, it is essential to put the name of the object on which you operate, that is, you must do for example: self.dato = 2 , and not dato=2 . This is different from other programming languages, where you can omit this .

  • The same to access other methods of the object from one of its methods, that is, you must put self.metodo2() , for example, and not metodo2() to dry.

  • answered by 28.08.2018 в 20:31
    1

    move is defined as an instance method (defined within the class definition), as every instance method receives a first mandatory parameter that is the instance of the class itself, which by convention is named by self , this parameter is passed automatically when the method is called through an instance ( instancia.method() ). In your case move is defined as:

    def move(argument)
    

    By not defining the parameter self , when you make player.move("ri") the instance of the class is automatically passed to the first argument that is argument , in addition there is the parameter that you pass "ri" , by so the function receives two parameters when it only has one defined, the reference to "ri" has nowhere to go because argument was already used by the reference to the instance player .

    Apart from this swither you must define it or within move or better in the __init__ as an instance attribute, apart the dictionary values are incorrect, they must be key: self.metodo .

    Finally;

    func = switcher.get(argumen, "Invalid Argument")
    return func()
    

    will fail if the key is not in the dictionary, if this happens get returns "Invalid Argument" , so in the next line you try to call a string:

    return "Invalid Argument"()
    

    what you can do is something like this:

    func = self._switcher.get(argumen)
    if func is None:    
        return "Invalid Argument"
    return func()
    
    class Robot:
    
        def __init__(self, x, y):
            self.x = x
            self.y = y
            self._switcher = {
                             "le": self.move_left,
                             "ri": self.move_right,
                             "up": self.move_up,
                             "do": self.move_down,
                             }
    
        @property 
        def x(self):
            return self._x
    
        @x.setter
        def x(self, x_value):
            if not  1 <= x_value <= 5:
                raise ValueError("x must be between 1 and 5 (both included)")
            self._x = x_value
    
        @property 
        def y(self):
            return self._y
    
        @y.setter
        def y(self, y_value):
            if not  1 <= y_value <= 5:
                raise ValueError("y must be between 1 and 5 (both included)")
            self._y = y_value
    
        def move_left(self):
            if (self._x == 1):
                return "You are in the left limit"
            else:
                self._x -= 1
                return "({},{})".format(self._x, self._y)
    
        def move_right(self):
            if (self._x == 5):
                return "You are in the rigth limit"
            else:
                self._x += 1
                return "({},{})".format(self._x, self._y)
    
        def move_up(self):
            if (self._y == 1):
                return "You are in the up limit"
            else:
                self._y -= 1
                return "({},{})".format(self._x, self._y)
    
        def move_down(self):
            if (self._y == 5):
                return "You are in the down limit"
            else:
                self._y += 1
                return "({},{})".format(self._x, self._y)
    
        def move(self, argumen):
            func = self._switcher.get(argumen)
            if func is None:
                return "Invalid Argument"
            return func()
    
    
    def main():
       player = Robot(1, 1)
       print(player.move("ri"))
       print(player.move("ri"))
       print(player.move("ri"))
       print(player.move("ri"))
       print(player.move("ri"))
       print(player.move("do"))
       print(player.move("up"))
       print(player.move("up"))
       print(player.move("le"))
       print(player.move("fo"))
    
       player2 = Robot(6, 1)
    
    if __name__ == "__main__":
       main()
    

    I have added a couple of properties to validate x e y in the instantiation and make sure they are between 1 and 5.

    The output for that main is:

    (2,1)
    (3,1)
    (4,1)
    (5,1)
    You are in the rigth limit
    (5,2)
    (5,1)
    You are in the up limit
    (4,1)
    Invalid Argument
    Traceback (most recent call last):
      File "test.py", line 84, in <module>
        main()
      File "test.py", line 81, in main
        player2 = Robot(6, 1)
      File "test.py", line 4, in __init__
        self.x = x
      File "test.py", line 20, in x
        raise ValueError("x must be between 1 and 5 (both included)")
    ValueError: x must be betwen 1 and 5 (both included)
    
        
    answered by 28.08.2018 в 20:28