1.15一个类的实例:继承、多态

2020-06-09  本文已影响0人  Benedict清水

前言

在本节中我们将编写两个类:

步骤一:创建实例

在python中,模块名使用小写字母开头,而类名使用大写字母开头,这是通用的惯例。我们新的模块文件的命名为person.py。

class Person: 

一个模块文件中可以编写任意多的函数和类,但是如果添加了太多与模块不相关的内容,那么我们person.py的名称就意义不大了。当模块拥有一个单一、内聚的用途的时候,他们工作的会更好。
编写构造函数
给实例属性赋初值得常见方法是在_init_构造函数方法中将初值赋给self。构造函数包含了每次创建一个实例时python都会自动运行的代码。

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

我们传入的数据作为构造函数方法的参数附加到一个实例上,并且将它们赋给self以长期持有。self就是新创建的实例对象,而name、job、pay则是状态信息,即保存在对象中供随后使用的描述性数据。
测试
编写测试代码:

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


if __name__ == "__main__":
    bob = Person("Bob Smith", "dev", "10k")
    sue = Person(name="Sue jones", job="test", pay="8k")
    print("{0} {1} {2}".format(bob.name, bob.job, bob.pay))
    print(sue.name, sue.job, sue.pay)

__name__模块检查用于解决只有当文件作为测试运行而不是导入的时候,才执行文件底部的测试语句。只有当把文件作为顶层脚本运行的时候才测试它,因为这个时候的__name__是__main__。但当做模块导入的时候,则__name__等于模块名称。
两种使用代码的方式:

C:\code>python person.py
Bob Smith dev 10k
Sue jones test 8k
Python 3.7.4 (default, Oct 12 2019, 19:06:48)
[Clang 11.0.0 (clang-1100.0.33.8)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import person
>>>

当被导入时,文件定义了类却没有使用它。当直接运行时,文件创建了类的两个实例,并且打印了每个实例的两个属性。

步骤二:添加行为方法

我们要把操作对象的代码编写到类方法中,而不是分散在整个整个程序中。这里就一种封装的思想,就是把操作逻辑的包装到接口之后,这样每种操作在我们的程序中只会编写一次,通过这种方式,如果将来需要修改,那么只需修改一个版本。

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

    def lastName(self):
        return self.name.split()[-1]

    def giveRaise(self, percent):
        self.pay = int(self.pay * (1 + percent))


if __name__ == "__main__":
    bob = Person("Bob Smith", "dev", 10000)
    sue = Person(name="Sue jones", job="test", pay=8000)
    print("{0} {1} {2}".format(bob.name, bob.job, bob.pay))
    print(sue.name, sue.job, sue.pay)
    print(bob.lastName(), sue.lastName())
    sue.giveRaise(0.10)
    print(sue.pay)
C:\code>python person.py
Bob Smith dev 10K
Sue jones test 8000
Smith jones
8800

方法只是附加给类并旨在处理类的实例的常规函数。实例是方法调用的主体,并且会自动传给方法的self参数。

步骤三:运算符重载

当我们需要跟踪一个对象的时候,我们发现实例对象默认的显示格式并不是很友好,它显示的是对象的类名以及在内存中的地址。

 print("bob:{}".format(bob))
 print("sue:{}".format(sue))
bob:<__main__.Person object at 0x1056621d0>
sue:<__main__.Person object at 0x105662210>

我们可以通过使用运算符重载来做出更友好的输出——在一个类中编写这样的方法,当方法在类的实例上运行的时候,方法拦截并处理内置的操作。我们可以使用排在__init__之后第二个常用的运算符重载方法__repr__,以及__repr__的孪生兄弟__str__。每次一个实例转化为其打印字符串的时候,该方法都会自动运行。打印一个对象会显示该对象的__str__或__repr__方法(要么是自己定义的,要么是从父类继承的)所返回的内容。

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

    def lastName(self):
        return self.name.split()[-1]

    def giveRaise(self, percent):
        self.pay = int(self.pay * (1 + percent))
    def __str__(self):
        return '[Person_str:%s, %s, %s]' % (self.name, self.job, self.pay)
    def __repr__(self):
        return '[Person_repr:%s, %s, %s]' % (self.name, self.job, self.pay)


if __name__ == "__main__":
    bob = Person("Bob Smith", "dev", 10000)
    sue = Person(name="Sue jones", job="test", pay=8000)
    print("bob:{}".format(bob))
    print("sue:{}".format(sue))

运行方式一

C:\code>python person.py
bob:[Person_str:Bob Smith, dev, 10000]
sue:[Person_str:Sue jones, test, 8000]

运行方式二

Python 3.7.4 (default, Oct 12 2019, 19:06:48)
[Clang 11.0.0 (clang-1100.0.33.8)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> from person import Person
>>> bob = Person("Bob Smith", "dev", 10000)
>>> bob
[Person_repr:Bob Smith, dev, 10000]
>>> print(bob)
[Person_str:Bob Smith, dev, 10000]

程序提示:调用print函数的时候使用__str__而交互式命令行调用__repr__。

步骤四:继承,编写子类(多态)

场景:我们来定义一个管理人员,管理人员每次涨工资的时候,除了像往常一样接受传入的百分比外,还会获得一个默认10%的额外奖金。即:如果一个管理人员加薪10%,那么它实际得到20%的增长。

class Manager(Person):
    def giveRaise(self, percent, bonus=.10):

现在有两种方式编写Manager的定制方法:一种好的方式和不好的方式。

class Manager(Person):
    def giveRaise(self, percent, bonus=.10):
        self.pay = int(self.pay * (1 + percent + bonus))
class Manager(Person):
    def giveRaise(self, percent, bonus=.10):
        Person.giveRaise(self, percent + bonus)

不好的方式是直接复制粘贴代码,基本会使未来的维护工作翻倍。通过类直接调用有效的避开了继承的限制,从而能将调用直接定位到类树的某个特定版本。对于通过类名调用的方式,你需要自己给self传入一个实例,Manger类中对于giveRaise这样的一个内部代码,self已经是调用的主体,因此直接可以通过self传入对应的实例。
下面是本模块完整的代码:

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

    def lastName(self):
        return self.name.split()[-1]

    def giveRaise(self, percent):
        self.pay = int(self.pay * (1 + percent))

    def __str__(self):
        return '[%s_str:%s, %s, %s]' % (self.__class__.__name__, self.name, self.job, self.pay)

    def __repr__(self):
        return '[%s_repr:%s, %s, %s]' % (self.__class__.__name__, self.name, self.job, self.pay)


class Manager(Person):
    def giveRaise(self, percent, bonus=.10):
        Person.giveRaise(self, percent + bonus)


if __name__ == "__main__":
    sue = Person(name="Sue jones", job="test", pay=10000)
    print("sue:{}".format(sue))
    sue.giveRaise(.10)
    print("sue:{}".format(sue))
    tom = Manager("Tom Jones", "mgr", 10000)
    print("tom:{}".format(tom))
    tom.giveRaise(.10)
    print("tom:{}".format(tom))
C:\code>python person.py
sue:[Person_str:Sue jones, test, 10000]
sue:[Person_str:Sue jones, test, 11000]
tom:[Manager_str:Tom Jones, mgr, 10000]
tom:[Manager_str:Tom Jones, mgr, 12000]

我们从上面的结果可以看到,sue被分配到person中的giveRaise方法,而tom被分配到Manager中的giveRaise的方法。根据所传递的对象的类型,将会自动运行合适的版本,这就是多态

步骤五:定制和调用父类的构造函数

我们创建Manager对象的时候,为它提供一个mgr的参数似乎是没有意义的,这已经包含在类中了。我们可以重新定制Manager中的__init__方法,提供mgr字符串。

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

    def lastName(self):
        return self.name.split()[-1]

    def giveRaise(self, percent):
        self.pay = int(self.pay * (1 + percent))

    def __str__(self):
        return '[%s_str:%s, %s, %s]' % (self.__class__.__name__, self.name, self.job, self.pay)

    def __repr__(self):
        return '[%s_repr:%s, %s, %s]' % (self.__class__.__name__, self.name, self.job, self.pay)


class Manager(Person):
    def __init__(self, name, pay):
        Person.__init__(self, name, 'mgr', pay)  # 调用父类的方法
    
    def giveRaise(self, percent, bonus=.10):
        Person.giveRaise(self, percent + bonus)


if __name__ == "__main__":
    sue = Person(name="Sue jones", job="test", pay=10000)
    print("sue:{}".format(sue))
    sue.giveRaise(.10)
    print("sue:{}".format(sue))
    tom = Manager("Tom Jones", 10000)  # 可以减少“mgr”参数
    print("tom:{}".format(tom))
    tom.giveRaise(.10)
    print("tom:{}".format(tom))
上一篇 下一篇

猜你喜欢

热点阅读