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

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.