Книга: Practical Programming, Fourth Edition
Назад: Function isinstance, Class object, and C lass Book
Дальше: Plugging into Python Syntax: Mo re Special Methods

Writing a Method in Class Book

As you saw in Chapter 7, , there are two ways to call a method. One way is to access the method through the class, and the other is to use object-oriented syntax. These two calls are equivalent:

 >>>​​ ​​str.capitalize(​​'browning'​​)
 'Browning'
 >>>​​ ​​'browning'​​.capitalize()
 'Browning'

You’d probably like to be able to write similar code involving the class Book. For example, you might want to ask how many authors a Book has:

 >>>​​ ​​Book.num_authors(ruby_book)
 3
 >>>​​ ​​ruby_book.num_authors()
 3

To get this to work, define a method called num_authors inside Book. Here it is:

 class​ Book:
 """Information about a book."""
 
 def​ ​num_authors​(self) -> int:
 """Return the number of authors of this book.
  """
 
 return​ len(self.authors)

The first argument of
a method must be
the object the method
is being called on

The Book method num_authors resembles a function except that it has a parameter called self, which refers to a Book object. Assuming this class is defined in the file book.py, you can import it, create a Book object, and call num_authors:

 >>>​​ ​​import​​ ​​book
 >>>​​ ​​ruby_book​​ ​​=​​ ​​book.Book()
 >>>​​ ​​ruby_book.title​​ ​​=​​ ​​'Programming Ruby'
 >>>​​ ​​ruby_book.authors​​ ​​=​​ ​​[​​'Thomas'​​,​​ ​​'Fowler'​​,​​ ​​'Hunt'​​]
 >>>​​ ​​ruby_book.num_authors()
 3

No Magic in self

icon indicating an aside

In Python, self is not a keyword (unlike this, which performs a similar function in Java and C++). You can use any other valid name for this parameter, even this:

 def​ ​num_authors​(this) -> int:
 return​ len(this.authors)

However, breaking the naming convention is strongly discouraged because it may confuse other Python programmers. Additionally, many software development tools expect a self.

The call lists the object first; think of it as asking the book how many authors it has. Thinking of method calls this way helps to develop an object-oriented mentality.

In the ruby_book example, the title and list of authors were assigned after the Book object was created. That approach isn’t scalable; you don’t want to have to type those extra assignment statements every time you create a Book. Instead, write a special initializer dunder (double underscore) method called __init__ that does this for you as you create the Book. Also, include the publisher, ISBN, and price as parameters of the __init__ method:

1: from​ ​typing​ ​import​ Any
class​ Book:
"""Information about a book, including title, list of authors,
5:  publisher, ISBN, and price.
"""
def​ ​__init__​(self, title: str, authors: list[str], publisher: str,
isbn: str, price: float) -> None:
10: """Initialize a new book entitled title, written by the people in
authors, published by publisher, with ISBN isbn and costing
price dollars.
>>> python_book = Book( ​​\
15:  'Practical Programming', ​​\
['Campbell', 'Gries', 'Montojo'], ​​\
'Pragmatic Bookshelf', ​​\
'978-1-6805026-8-8', ​​\
25.0)
20:  >>> python_book.title
'Practical Programming'
>>> python_book.authors
['Campbell', 'Gries', 'Montojo']
>>> python_book.publisher
25:  'Pragmatic Bookshelf'
>>> python_book.ISBN
'978-1-6805026-8-8'
>>> python_book.price
25.0
30: 
"""
self.title = title
# Copy the authors list in case the caller modifies that list later.
35:  self.authors = authors.copy()
self.publisher = publisher
self.ISBN = isbn
self.price = price
40: def​ ​num_authors​(self) -> int:
"""Return the number of authors of this book.
>>> python_book = Book( ​​\
'Practical Programming', ​​\
45:  ['Campbell', 'Gries', 'Montojo'], ​​\
'Pragmatic Bookshelf', ​​\
'978-1-6805026-8-8', ​​\
25.0)
>>> python_book.num_authors()
50:  3
"""
return​ len(self.authors)

Notice that you can include doctests for methods just as you do for functions. Notice also that you should not specify the type of the first parameter of a method, since its type is always the class in which it is defined.

Also notice the call to the list.copy on line 35. Without this call, the variable self.authors becomes an alias to authors and will change at will if the authors variable is mutated.

This module contains a single (complicated) statement: the class definition. When Python executes this module, it creates a class object and assigns it to the variable Book:

Book

Method __init__ is called whenever a Book object is created. Its purpose is to initialize the new object. Here are the steps that Python follows when creating an object:

  1. It creates an object at a particular memory address.
  2. It calls method __init__, passing in the new object into the parameter self.
  3. It produces that object’s memory address.

Initializer Constructor

icon indicating an aside

The __init__ method is sometimes referred to as a constructor—but, strictly speaking, it is not. The __new__ method creates and returns a new object, while the __init__ method initializes the new instance after it has been created.

Let’s try it out in the shell:

 >>>​​ ​​import​​ ​​book
 >>>​​ ​​python_book​​ ​​=​​ ​​book.Book(
 ...​​ ​​'Practical Programming'​​,
 ...​​ ​​[​​'Campbell'​​,​​ ​​'Gries'​​,​​ ​​'Montojo'​​],
 ...​​ ​​'Pragmatic Bookshelf'​​,
 ...​​ ​​'978-1-6805026-8-8'​​,
 ...​​ ​​25.0)
 >>>​​ ​​python_book.title
 'Practical Programming'
 >>>​​ ​​python_book.authors
 ['Campbell', 'Gries', 'Montojo']
 >>>​​ ​​python_book.publisher
 'Pragmatic Bookshelf'
 >>>​​ ​​python_book.ISBN
 '978-1-6805026-8-8'
 >>>​​ ​​python_book.price
 25.0

The following image illustrates the memory model generated by this code:

Book

Let’s trace method call python_book.num_authors. Python first finds the object that python_book refers to and calls its method num_authors. There are no explicit arguments, so Python only passes in the Book object that python_book refers to, assigning that object to the self parameter:

Book

The return statement, return len(self.authors), is then executed. The expression, len(self.authors), is a function call. Python evaluates the argument, self.authors, by finding the object that self refers to and then, in that object, finds the instance variable authors. The variable is a list, and the length of that list is the value that Python returns, as shown here:

Book

With initializers, methods, and instance variables in hand, you can now create classes that look and work like those that come with Python itself.

Назад: Function isinstance, Class object, and C lass Book
Дальше: Plugging into Python Syntax: Mo re Special Methods