[Tutor] method, type?

Cameron Simpson cs at zip.com.au
Wed Jan 6 00:40:35 EST 2016


On 05Jan2016 20:58, Alex Kleider <akleider at sonic.net> wrote:
>#!/usr/bin/env python3
># OS: Ubuntu 10.4LTS
>
># My code:
>
>class JournalLineItem(object):
>    """
>    """
>
>    def __init__(self, account, debit_or_credit, amount):
>        self.account = account
>        self.debit_or_credit = debit_or_credit
>        self.amount = float(amount)
>
>    def show(self):
>        return ("ACNT: {}  ${:0.2f} {}".
>            format(self.account, self.amount, self.debit_or_credit))
>
>    def get_line_item(text):
>        return JournalLineItem(*text.split())
>
>def test():
>    print(
>    JournalLineItem.get_line_item("2435 Dr 25.33").show())
>
>if __name__ == "__main__":
>    test()
>
>    myquestion = """
>
>What kind of a method/function is get_line_item?

As written, it is an instance menthod that _thinks_ it is a static method. Not 
that that was what you intended :-)

>From what I've read (and not fully understood)
>static methods and class methods must have
>@staticmethod and @classmethod on the line above them.

As written, you could put @staticmethod above it - it is an ordinary function 
that resides in the class to associate its functionality with the class.

However, what you are probably better off with is @classmethod, for reasons I 
will explain below.

Instance methods expect their first parameter to be the instance (self) and 
class methods expect their first parameter to be the class. This is arranged 
implicitly when you call the method via an instance or class.

>get_line_item works as I wanted but it's clearly not the
>usual type of method and I don't know how to categorize it.

It is confused. There is nothing in the Python language that requires the 
instance to be called "self", that is just convention. So your method thinks 
that the instance is "text", and works on that. So you could do this:

  jli = JournalLineItem(account, debit_or_credit, amount)
  jli.get_line_item()

and the call would go ahead, but fail because a JournalLineItem has no .split 
method. And you can't do this:

  jli = JournalLineItem(account, debit_or_credit, amount)
  jli.get_line_item("2435 Dr 25.33")

because, since it is an instance method, Python implicitly passes in the 
instance (jli) as the first parameter, so the call is equivalent to:

  JournalLineItem.get_line_item(jil, "2435 Dr 25.33")

but the function only expected a single parameter. Badness ensues.

>It's an instance creator- is there a better term?

Generally these are called factories. Normally you would either write this as a 
class method:

  @classmethod
  def get_line_item(cls, text):
    return cls(*text.split())

which will get JournalLineItem when you call it as in your example:

  item = JournalLineItem.get_line_item("2435 Dr 25.33")

but will get the right subclass if you subclass JournalLineItem:

  class SpecialJournalLineItem(JournalLineItem):
    ...

  special_item = SpecialJournalLineItem.get_line_item("2435 Dr 25.33")

Alternatively, if you never expect to subclass this, you could just declare it 
as a normal function outside the class entirely:

  def get_line_item(cls, text):
    return JournalLineItem(*text.split())

Often these functions or methods are called from_blah, so the outside-the-class 
function might be called "JournalLineItem_from_text", and the inside the class 
@classmethod one might be called "from_text". So you might write:

  jli = JournalLineItem.from_text("2435 Dr 25.33")

which reads well and allows you to subclass JournalLineItem later. Therefore I 
would recommend the @classmethod approach. And stylisticly, I would put that up 
the top, just under __init__.

Hoping this helps rather than confuses,
Cameron Simpson <cs at zip.com.au>


More information about the Tutor mailing list