0

I have the following code:

class Player():
    """Initialization examples:
            player = Player('Lebron', 'James')
            player = Player.by_id(2544)
    """
    def __init__(self, first_name, last_name):
        """Search for a given player matching first_name and last_name.

        The matching is case insensitive.
        """
        self.player = self._get_player_by_name(
            first_name.lower(), last_name.lower())

    @classmethod
    def by_id(self, player_id):
        """Search for a given player by thier nba.com player id.

        Args:
            player_id: str representation of an nba.com player id
                e.g. Lebron's player id is 2544 and his nba.com link is
                    https://stats.nba.com/player/2544/

        Intended as an alternate Player constructor.
        """
        self.player = self._get_player_by_id(str(player_id))

    def _get_player_by_id(self, player_id):
        pass

However, when calling Player.by_id(2544), I get the following error:

TypeError: _get_player_by_id() missing 1 required positional argument: 'player_id'

What's going on here? Most questions I've searched just involve adding the self argument, which I already have.

1
  • One thing that would dramatically improve clarity for you here: The first argument received by by_id is not an instance of Player, so don't call it self. It's Player itself (or a subclass thereof), and should be named cls to indicate it's a class, not an instance. Trying to call instance methods on classes doesn't work unless you pass an instance as the first argument, and you didn't. Commented Nov 26, 2019 at 2:00

2 Answers 2

1

@classmethod causes the method to take the class type as the first parameter, instead of a specific instance. For example, consider the following code:

class C:
    @staticmethod
    def a():
        # static methods take no implicit parameters
        print("a")

    @classmethod
    def b(cls):
        # class methods implicitly take the *class object* as the first parameter
        print("b", cls)

    def c(self):
        # instance methods implicitly take the instance as the first parameter
        print("c", self)

C().a()
C().b()
C().c()

This prints

a
b <class '__main__.C'>
c <__main__.C object at 0x103372438>

Note that, by convention, we use cls instead of self for class methods. This is just convention - calling this parameter self does not magically make it an instance!

This means that, in by_id, when you call self._get_player_by_id you are calling Player._get_player_by_id - without an instance. That means that player_id ends up being passed as "self", resulting in the error you see.

To fix this you'll probably want _get_player_by_id to be a class method too.

Sign up to request clarification or add additional context in comments.

Comments

0

by_id is a class method, as you know by calling the method with the class name.

However, this class method then calls an instance specific method,

_get_player_by_id.

What's happening is that, when _get_player_by_id is ran, you're providing the player_id as a parameter argument to self, not the actual player_id.

And this makes sense, because self is in reference to the instance, not the class object.

What you need to do is, in your by_id method, you need to pass in the instance of the Player, and pass that into get_player_by_id along with the player_id

1 Comment

Would you mind adding the code fix? I'm not sure I follow. I also switched the first argument of by_id to cls per @ShadowRanger's comment

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.