Step 2: Adding Behavior Methods

Everything looks good so far—at this point, our class is essentially a record factory; it creates and fills out fields of records (attributes of instances, in more Pythonic terms). Even as limited as it is, though, we can still run some operations on its objects. Although classes add an extra layer of structure, they ultimately do most of their work by embedding and processing basic core data types like lists and strings. In other words, if you already know how to use Python’s simple core types, you already know much of the Python class story; classes are really just a minor structural extension.

For example, the name field of our objects is a simple string, so we can extract last names from our objects by splitting on spaces and indexing. These are all core data type operations, which work whether their subjects are embedded in class instances or not:

广告:个人专属 VPN,独立 IP,无限流量,多机房切换,还可以屏蔽广告和恶意软件,每月最低仅 5 美元

>>> name = 'Bob Smith'      # Simple string, outside class
>>> name.split()            # Extract last name
['Bob', 'Smith']
>>> name.split()[-1]        # Or [1], if always just two parts
'Smith'

Similarly, we can give an object a pay raise by updating its pay field—that is, by changing its state information in-place with an assignment. This task also involves basic operations that work on Python’s core objects, regardless of whether they are standalone or embedded in a class structure:

>>> pay = 100000            # Simple variable, outside class
>>> pay *= 1.10             # Give a 10% raise
>>> print(pay)              # Or: pay = pay * 1.10, if you like to type
110000.0                    # Or: pay = pay + (pay * .10), if you _really_ do!

To apply these operations to the Person objects created by our script, simply do to bob.name and sue.pay what we just did to name and pay. The operations are the same, but the subject objects are attached to attributes in our class structure:

# Process embedded built-in types: strings, mutability

class Person:
    def __init__(self, name, job=None, pay=0):
        self.name = name
        self.job  = job
        self.pay  = pay

if __name__ == '__main__':
    bob = Person('Bob Smith')
    sue = Person('Sue Jones', job='dev', pay=100000)
    print(bob.name, bob.pay)
    print(sue.name, sue.pay)
    print(bob.name.split()[-1])            # Extract object's last name
    sue.pay *= 1.10                        # Give this object a raise
    print(sue.pay)

We’ve added the last two lines here; when they’re run, we extract bob’s last name by using basic string and list operations and give sue a pay raise by modifying her pay attribute in-place with basic number operations. In a sense, sue is also a mutable object—her state changes in-place just like a list after an append call:

Bob Smith 0
Sue Jones 100000
Smith
110000.0

The preceding code works as planned, but if you show it to a veteran software developer he’ll probably tell you that its general approach is not a great idea in practice. Hardcoding operations like these outside of the class can lead to maintenance problems in the future.

For example, what if you’ve hardcoded the last-name-extraction formula at many different places in your program? If you ever need to change the way it works (to support a new name structure, for instance), you’ll need to hunt down and update every occurrence. Similarly, if the pay-raise code ever changes (e.g., to require approval or database updates), you may have multiple copies to modify. Just finding all the appearances of such code may be problematic in larger programs—they may be scattered across many files, split into individual steps, and so on.